2016-08-19 8 views
3

WPFとMVVMを使用してタンク内の液体のレベルを表示するという明らかに単純な問題に取り組んでいます。ViewModelの情報をビューに表示する方法

タンクを表示するために、内部に長方形があるDockPanelを使用しました。長方形の高さは、タンク内の液体の量に基づいて変化します。タンクの上部には、タンクに入っている液体の量を示すTextBlockがあります。 私はこのような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> 

あなたは私はMVVMパターン以下、TankViewModelクラスとTankModelクラスを作成していることもわかります。

TextBlockに液体の量を表示することは簡単で、データバインディングはジョブを完全に行います。しかし、矩形の高さに関しては、ViewとViewModelの間の問題を適切に分離する方法が見つからないため、いくつかの問題が発生します。

Rectangleの高さはこのように、私はタンクが満たされているものの割合で私に語っ数を得ることができ、この方法では、タンク内の最大容量にして存在する液体の量に依存:

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)); 
     } 
    } 
} 

ただし、このプロパティを矩形の高さに直接バインドすることはできません。そのようなプロパティに値を与えると、Viewが担当するように聞こえるようになります。 これを達成するには、このパーセンテージ値を矩形の高さに変換するビューにコードを挿入する必要があります。

ViewのOnPropertyChanged()コールバックを実装してこれを実現できますか?

私が設置したアーキテクチャを簡素化する方法について提案がありますか?

答えて

2

roryを補完する。固定のConverterパラメータを必要とせず、一部のコンテナからの実際の高さを使用する場合は、MultiBindingIMultiValueConverterを使用することもできます。

これにより、親の実際の高さを使用して液体の高さを設定することができます。

私の例では、境界線を持つグリッドを使用してGUIの液体タンクを表しています。

のViewModel:

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(); 
    } 
} 

XAML:

<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> 
+0

私は、アカウントに、コンテナの実際の高さを取りたいので、私は受け入れソリューションとしてこれをマークそれをハードコーディングすることはできません。 Roryとtoadflakzの回答も非常に役に立ちました、ありがとう! – Perennialista

4

これは、ビューモデルからのパーセント値を使用して簡単に計算できます。あなたはビュー内の矩形の最大高さを知っています(おそらく静的な値です)。次に、現在の高さ=最大高さにパーセンテージ値を掛けた値。

XAMLでこれを行う操作は、the Binding.Converter property with an IValueConverterを使用して行うことができます。関連するthis postを参照してください。ここで

は、例えば、コンバータです:

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です:

<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を残してきた

<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ですこれには1つのプロパティ:OilLevelしかありません。

1

ValueConverterを使用すると、ViewModelの値がIntFilledPercentageに変換され、長方形の表示値がHeightになります。

あなたはRectangleHeightプロパティにIntFilledPercentageのViewModelプロパティをバインドしConverterクラスで実際の視覚的な単位にパーセントからの変換を行うだろう。

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

コンバータはIValueConverterインターフェイスを実装しています。この場合、実装する必要があるのはConvert()です。

関連する問題