C# 如何将信息从ViewModel转换为在视图中显示

C# 如何将信息从ViewModel转换为在视图中显示,c#,wpf,mvvm,C#,Wpf,Mvvm,我正在处理一个显然很简单的问题,即使用WPF和MVVM显示储罐中的液位 为了展示一个坦克,我使用了一个里面有一个矩形的船坞面板。矩形的高度会根据储罐中的液体量而变化。在油箱顶部有一个文本块,显示油箱中的液体量。 我在XAML中定义如下: <DockPanel x:Name="tankView" HorizontalAlignment="Left" Height="212" VerticalAlignment="Top" Width="144" DataContext="{Binding S

我正在处理一个显然很简单的问题,即使用WPF和MVVM显示储罐中的液位

为了展示一个坦克,我使用了一个里面有一个矩形的船坞面板。矩形的高度会根据储罐中的液体量而变化。在油箱顶部有一个文本块,显示油箱中的液体量。 我在XAML中定义如下:

<DockPanel x:Name="tankView" HorizontalAlignment="Left" Height="212" VerticalAlignment="Top" Width="144" DataContext="{Binding Source={StaticResource TankViewModel}}">
            <TextBlock x:Name="oilQuantity" HorizontalAlignment="Right" VerticalAlignment="Top" DockPanel.Dock="Top" Margin="0,0,10,0" Text = "{Binding TxtOilQuantity, Mode=OneWay}"/>
            <Rectangle x:Name="oilLevel" Fill="Green" Height="66" Stroke="Black" VerticalAlignment="Bottom" HorizontalAlignment="Stretch" DockPanel.Dock="Bottom"/>
</DockPanel>
public class TankViewModel : INotifyPropertyChanged
{
    private TankModel tankModel = new TankModel(2500);

    public int IntFilledPercentage {
        get {
            if (tankModel.OilQuantity == 0)
                return 0;
            else
                return Convert.ToInt32(((double)tankModel.OilQuantity / tankModel.capacity) * 100);
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void NotifyPropertyChanged(String info) {
        if (PropertyChanged != null) {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
}
但是,我不能将这个属性直接绑定到矩形的高度,给这样的属性赋值听起来像是视图应该负责的事情。 为了实现这一点,我必须在视图中插入一些代码,将这个百分比值转换为矩形的高度

我可以通过实现视图的OnPropertyChanged()回调来实现这一点吗


对于如何简化我所建立的体系结构,您有什么建议吗

您可以通过使用视图模型中的百分比值,通过使用一点数学运算,轻松完成此操作。您知道视图中矩形的最大高度(可能是静态值)。然后,当前高度=最大高度乘以百分比值

在XAML中执行这样的操作可以使用。看,这是相关的

下面是一个转换器示例:

internal sealed class OilLevelConverter : System.Windows.Data.IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var percentage = (decimal) value;
        var maxLevel = System.Convert.ToInt32((string) parameter);
        var currentLevel = System.Convert.ToInt32(maxLevel * percentage);
        return currentLevel;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
这是你的
App.xaml

<Application x:Class="WpfApplication1.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WpfApplication1"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <local:ViewModel x:Key="ViewModel" />
        <local:OilLevelConverter x:Key="OilLevelConverter"/>
    </Application.Resources>
</Application>

下面是一个示例窗口XAML:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" 
        Width="525" 
        DataContext="{StaticResource ViewModel}" >

    <Grid>
        <Rectangle Fill="#FFF4F4F5" 
                   HorizontalAlignment="Left" 
                   Height="{Binding Path=OilLevel, 
                    Converter={StaticResource OilLevelConverter}, 
                    ConverterParameter=100}" Margin="183,132,0,0" 
                    Stroke="Black" VerticalAlignment="Top" Width="100"/>
    </Grid>
</Window>


注意:我省略了只有一个属性的
ViewModel
OilLevel

使用
ValueConverter
将矩形的
IntFilledPercentage
填充的视图模型值转换为
高度的视图值

您可以将
IntFilledPercentage
ViewModel属性绑定到
Rectangle
Height
属性,然后在
Converter
类中执行从百分比到实际视觉单位的转换

<Rectangle x:Name="oilLevel" Fill="Green" Height="{Binding IntFilledPercentage, Mode=OneWay, Converter={StaticResource PercentageToHeightConverter}" Stroke="Black" VerticalAlignment="Bottom" HorizontalAlignment="Stretch" DockPanel.Dock="Bottom"/>


转换器实现接口。在这种情况下,您只需要实现
Convert()

来补充rory。当您不需要固定的转换器参数,而是使用某个容器的实际高度时,也可以使用
MultiBinding
IMultiValueConverter

这允许使用母体的实际高度设置液体的高度

在我的示例中,我使用带边框的网格在gui上表示液体储罐

视图模型:

public class MainWindowViewModel : PropertyChangedBase // from Calburn.Micro (see nuget)
{
    private int _liquidPerc;

    public MainWindowViewModel()
    {
        LiquidPercentage = 25;
    }

    public int LiquidPercentage
    {
        get { return _liquidPerc; }
        set
        {
            if (value == _liquidPerc) return;
            _liquidPerc= value;
            NotifyOfPropertyChange(() => LiquidPercentage);
        }
    }
}
转换器:

/// <summary>
/// Converter which expects two params. percentage and maximum height
/// </summary>
public class LiquidLevelConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        var percentage = (int) values[0];
        var maxHeight = (double) values[1];
        return percentage*maxHeight*0.01;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
//
///需要两个参数的转换器。百分比和最大高度
/// 
公共类液位转换器:IMultiValueConverter
{
公共对象转换(对象[]值,类型targetType,对象参数,CultureInfo区域性)
{
风险值百分比=(整数)值[0];
var maxHeight=(双精度)值[1];
返回百分比*maxHeight*0.01;
}
公共对象[]转换回(对象值,类型[]目标类型,对象参数,CultureInfo区域性)
{
抛出新的NotImplementedException();
}
}
XAML:


我将此标记为可接受的解决方案,因为我想考虑容器的实际高度,而不是硬编码。Rory和toadflakz的回答也很有帮助,谢谢!
<Window x:Class="UiLiquedTankDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:UiLiquedTankDemo"
        xmlns:system="clr-namespace:System;assembly=mscorlib"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <local:MainWindowViewModel x:Key="ViewModel" />
        <local:LiquidLevelConverter x:Key="LiquidLevelConverter" />
    </Window.Resources>
    <DockPanel DataContext="{StaticResource ViewModel}">

        <!-- move the slider to move the level of the liquid --> 
        <Slider Minimum="0" Maximum="100" Value="{Binding LiquidPercentage}" 
                DockPanel.Dock="Bottom" 
                Margin="0"/>

        <!-- Liquid container representation using a grid --> 
        <Grid Name="LiquidContainer" Margin="200,5">
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>

            <Border Grid.Row="1" Background="Blue" Margin="0">
                <Border.Height>
                    <MultiBinding Converter="{StaticResource LiquidLevelConverter}">
                        <Binding Path="LiquidPercentage"></Binding>
                        <Binding Path="ActualHeight" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type Grid}}"></Binding>
                    </MultiBinding>
                </Border.Height>
            </Border>

            <Border Grid.Row="0" Grid.RowSpan="2"  BorderBrush="Black" BorderThickness="1" />
        </Grid>

    </DockPanel>
</Window>