让我们有两个枚举和一个模型类:
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];
}
}
}
假设我们想要实现一个包含一组RadioButtons用于枚举A的视图,对于每个枚举B,我们想要一个TextBox。
更改单选按钮组中的单选按钮将允许在文本框中编辑不同的值集,更改后将显示先前输入的值。
我能想到的最简单的方法是:
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
}
观点:
<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创建一个属性,并在radiobutton组选择发生变化时执行PropertyNotification。
问题:
是否可以以这样的方式执行此操作:ViewModel仅显示单个属性而不是三个属性(ValueB1,ValueB2,ValueB3):
这个单一属性将返回一个ObservableCollection&lt;&gt;或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 [参数],但这是我对此感到困惑的另一种方式... 即使我实现了ConvertBack方法,我只是简单地更新ObservableCollection而不是模型类中的值本身。
如何做到这一点?是否存在我缺少的常见模式
答案 0 :(得分:0)
首先,我道歉:我在离开办公室前的最后一刻回答。
所以,这就是我如何解决你的问题,但还有很多其他的解决方案。这是一种粗略但最小化的方式来创建一个GUI(据我所知)。
让我们从viewmodels开始:你的案例看起来是一个简单的主 - 细节上下文,但也可以看作是一个分层上下文。
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”,尽管目标值应限制为浮点数。那是因为总是接受一个字符串,你可以实现自己的验证逻辑。相反,数字属性可能会导致异常,而输入的字符不允许作为数字。
上面的视图模型以下列方式填充(例如):
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>
视觉效果如下图所示。