c# - 替换: One Property in ViewModel for each TextBox with B: One property returning a (observable) collection

标签 c# wpf mvvm binding properties

让我们拥有这两个枚举和一个模型类:

public enum A
{
    A1,
    A2,
    A3,
    A4
}
public enum B
{
    B1,
    B2,
    B3
}

// model class
public class MyModel
{
    private float[][] array;

    public MyModel()
    {
        array = new float[Enum.GetNames(typeof(A)).Length][];
        foreach (A a in EnumUtil.GetValues<A>())
        {
            array[(int) a] = new float[Enum.GetNames(typeof(B)).Length];
        }
    }

    public A EnumerationA { get; set; }
    public B EnumerationB { get; set; }
    public float this[A a, B b]
    {
        get
        {
            return array[(int) a][(int) b];
        }
        set
        {
            array[(int)a][(int)b] = value;
        }
    }
    public float[] ArraySlice
    {
        get
        {
            return array[(int) EnumerationA];
        }
    }
}

假设我们要为一个枚举A实现一个具有一组RadioButton的 View ,对于每个枚举B我们都希望一个TextBox。
更改单选按钮组中的单选按钮将允许在TextBoxes中编辑不同的值集,而改回将显示先前输入的值。

我能想到的最简单的方法是:
public class MyViewModel : ViewModelBase
{
    private MyModel _myModel;

    public A EnumerationA
    {
        get
        {
            return _myModel.EnumerationA;
        }
        set
        {
            if (Enum.Equals(_myModel.EnumerationA, value) == false)
            {
                _myModel.EnumerationA = value;
                RaisePropertyChanged("EnumerationA");
            }
        }
    }

    public float ValueB1
    {
        get
        {
            return _myModel[EnumerationA, B.B1];
        }
        set
        {
            if (_myModel[EnumerationA, B.B1] != value)
            {
                _myModel[EnumerationA, B.B1] = value;
                RaisePropertyChanged("ValueB1");
            }
        }
    }
    // Analogously for ValueB2 and ValueB3 properties

}

和 View :
<Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto"/>      
      <RowDefinition Height="Auto"/>

    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto"/>
      <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>

  <Label Grid.Row="0" Grid.Column="0" Content="Enum A" />  
  <StackPanel Grid.Row="0" Grid.Column="1" Orientation="Vertical">
    <RadioButton IsChecked="{Binding Path=EnumerationA, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static model:A.A1}}" Content="A1" />
    <RadioButton IsChecked="{Binding Path=EnumerationA, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static model:A.A2}}" Content="A2" />
    <RadioButton IsChecked="{Binding Path=EnumerationA, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static model:A.A3}}" Content="A3" />
    <RadioButton IsChecked="{Binding Path=EnumerationA, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static model:A.A4}}" Content="A4 />
  </StackPanel>

  <Label Grid.Row="1" Grid.Column="0" Content="Float Values" />  
  <StackPanel Grid.Row="1" Grid.Column="1" Orientation="Vertical">
    <StackPanel Orientation="Horizontal"><Label Content="B1" /><TextBox Text="{Binding Path=ValueB1}" /></StackPanel>
    <StackPanel Orientation="Horizontal"><Label Content="B2" /><TextBox Text="{Binding Path=ValueB2}" /></StackPanel>
    <StackPanel Orientation="Horizontal"><Label Content="B3" /><TextBox Text="{Binding Path=ValueB3}" /></StackPanel>    
  </StackPanel>

 </Grid>

即,此方法为每个TextBox创建一个属性,并在单选按钮组选择更改时执行PropertyNotification。

问题:
是否可以通过这样的方式做到这一点,即ViewModel仅公开单个属性,而不公开三个属性(ValueB1,ValueB2,ValueB3):
这个单一属性将返回ObservableCollection <>或IList(或与索引选择类似的东西)?


我考虑了一种可能的方法:
在VM中,我们会有类似
    public ObservableCollection<float> ArraySlice
    {
        get
        {
            return new ObservableCollection<float>(_myModel.ArraySlice);
        }

    }

在视野中
<StackPanel Orientation="Horizontal">
<Label Content="B1" />
<TextBox Text="{Binding Path=WindSpeedAverage, Converter={StaticResource EnumToObservableCollectionConverter}, ConverterParameter={x:Static model:B.B1}}" />
</StackPanel>

这个EnumToObservableCollectionConverter-一种简单的方法,它将值转换为IList并简单地返回IList [parameter],但这是我感到困惑的另一种方法...
即使实现了ConvertBack方法,我也将只更新ObservableCollection,而不更新模型类中的值本身。

怎么做?有没有我缺少的常见模式

最佳答案

首先,我道歉:我在离开办公室前的最后一分钟回答。

因此,这就是我如何解决您的问题,但是还有许多其他解决方案。这是您创建GUI的一种粗略而最少的方法(据我所知,这是正确的)。

让我们从 View 模型开始:您的案例看起来像一个简单的主从上下文,但是它也可以看作是分层上下文。

public class VM
{
    public VM()
    {
        this._aItems = new ObservableCollection<VMA>();
    }

    private ObservableCollection<VMA> _aItems;

    public Collection<VMA> AItems
    {
        get { return this._aItems; }
    }
}


public class VMA
{
    public VMA()
    {
        this._bItems = new ObservableCollection<VMB>();
    }

    public string Title { get; set; }

    private ObservableCollection<VMB> _bItems;

    public Collection<VMB> BItems
    {
        get { return this._bItems; }
    }
}


public class VMB : INotifyPropertyChanged
{
    public string Title { get; set; }

    private string _value;
    public string Value
    {
        get { return this._value; }
        set
        {
            if (this._value != value)
            {
                this._value = value;
                this.OnPropertyChanged("Value");
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string name)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(
                this,
                new PropertyChangedEventArgs(name)
                );
        }
    }
}

请注意,尽管目标值应限制为浮点型,但“Value”属性的类型为“string”。这是因为字符串始终被接受,并且您可以实现自己的验证逻辑。取而代之的是,数字属性可能会导致异常,而输入的字符则不允许作为数字。

上面的 View 模型以以下方式填充(例如):
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        var rnd = new Random();
        var vm = new VM();
        this.DataContext = vm;

        //enum "A"
        foreach (var a in Enum.GetNames(typeof(A)))
        {
            var vma = new VMA();
            vma.Title = a;
            vm.AItems.Add(vma);

            //enum "B"
            foreach (var b in Enum.GetNames(typeof(B)))
            {
                var vmb = new VMB();
                vmb.Title = b;
                vmb.Value = rnd.Next(1000).ToString();
                vma.BItems.Add(vmb);
            }
        }
    }
}

请注意,为简化起见,我没有使用该模型,但是您可以随时添加它。相反,我选择使用随机整数填充。

最后,这是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"
        >

    <Window.Resources>

        <DataTemplate x:Key="tplA">
            <RadioButton
                Content="{Binding Path=Title}"
                IsChecked="{Binding Path=IsSelected, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ListBoxItem}}"
                />
        </DataTemplate>

        <DataTemplate x:Key="tplB">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Path=Title}" Width="30" />
                <TextBox Text="{Binding Path=Value}" Width="100" />
            </StackPanel>
        </DataTemplate>

    </Window.Resources>

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <ListBox
            ItemsSource="{Binding Path=AItems}"
            ItemTemplate="{StaticResource tplA}"
            Width="150"
            Height="200"
            Grid.Column="0"
            x:Name="L1"
            >
        </ListBox>

        <ListBox
            ItemsSource="{Binding Path=SelectedItem.BItems, ElementName=L1}"
            ItemTemplate="{StaticResource tplB}"
            Width="150"
            Height="200"
            Grid.Column="1"
            >
        </ListBox>
    </Grid>
</Window>

视觉结果如下图所示。

关于c# - 替换: One Property in ViewModel for each TextBox with B: One property returning a (observable) collection,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20149433/

相关文章:

c# - 如何向 EF 中包含的实体添加 where 子句?

c# - C# 中的控制台窗口?

c# - 根据 ViewModel 属性更改画笔

android - 使用 MVVM 模式显示和隐藏进度条

javascript - AngularJS 浏览器使用指令自动填充解决方法

c# - Silverlight 独立存储最佳实践?

c# - 如何在 ASP.Net 和 C# 网页中将 Reporting Services 报告显示为内联 PDF?

wpf - 将字节数组转换为 WPF 位图的最简单方法是什么

c# - 免费 WPF 实时图表

wpf - 值对象和 View 模型属性