让我们拥有这两个枚举和一个模型类:
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/