我有一个与模型绑定的表单。有一个标准的基本模型和少数儿童模型(附加字段)。
在模型控件上方有一个单选按钮组,在选择其中一个时,会出现前面提到的附加字段(在本例中为句子字段)。
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void sentencedPersonRadioButton_Checked(object sender, RoutedEventArgs e)
{
sentenceTextBox.Visibility = Visibility.Visible;
DataContext = new SentencedPerson();
}
private void personRadioButton_Checked(object sender, RoutedEventArgs e)
{
sentenceTextBox.Visibility = Visibility.Hidden;
DataContext = new Person();
}
}
让我们说有一个人和SentencedPerson:
public class Person: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private String name;
public String Name
{
get
{
return name;
}
set
{
if (value == name)
{
return;
}
name = value;
OnPropertyChanged("Name");
}
}
}
public class SentencedPerson : Person
{
private String sentence;
public String Sentence
{
get
{
return sentence;
}
set
{
if (value == sentence)
{
return;
}
sentence = value;
OnPropertyChanged("Sentence");
}
}
}
设计此类连接的正确方法是什么?添加新的'已检查'事件处理程序感觉很麻烦...我听说过MVVM模式,里面会有Person和SentencedPerson道具的某种PersonContext。但它并没有改变“检查”的需要。事件
也知道存在问题,因为公共字段的值是在设置新的DataContext之后。
答案 0 :(得分:1)
这是一个相当广泛的问题,但我会给你一些指示。
MVVM是 推荐的设计模式,用于构建基于XAML的应用程序。
您可以使用" CurrentSelectedContent"创建一个视图模型类。类型为object
或Person
的属性以及将enum
绑定到的RadioButton
属性。
有关更多信息以及如何使用MVVM将RadioButton
绑定到enum
源属性的示例,请参阅以下链接:
How to bind RadioButtons to an enum?
完成此操作后,您可以设置" CurrentSelectedContent"的值。属性基于视图模型中enum
源属性的setter中的单选按钮选择:
private MyLovelyEnum _enum;
public MyLovelyEnum VeryLovelyEnum
{
get
{
return _enum;
}
set
{
_enum = value;
switch (value)
{
case MyLovelyEnum.Person:
CurrentSelectedContent = new Person();
break;
//...
}
OnPropertyChanged("VeryLovelyEnum");
}
}
确保" CurrentSelectedContent" property引发PropertyChanged
事件,视图模型类实现INotifyPropertyChanged
接口。
在视图中,您可以使用ContentControl
并将其Content
属性绑定到" CurrentSelectedContent"属性:
<ContentControl Content="{Binding Content}">
<ContentControl.ContentTemplate>
<DataTemplate>
<TextBox Text="{Binding Name}" />
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
另外,请确保将视图的DataContext
设置为视图模型的实例:
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
这是如何使用MVVM模式执行此操作的粗略思路。您不是在视图的代码隐藏中处理事件而是绑定到源属性,而不是显式设置特定UI元素的DataContext
属性,而是将Content
的{{1}}属性绑定到您在视图模型类中创建的对象。
希望有所帮助。
答案 1 :(得分:0)
你只需要一个型号:
public class Person : INotifyPropertyChanged
{
string _name;
public string Name { get { return _name; } set { _name = value; RaisePropertyChanged("Name"); } }
bool _isSentenced;
public bool IsSentenced { get { return _isSentenced; } set { _isSentenced = value; RaisePropertyChanged("IsSentenced"); } }
string _sentence;
public string Sentence { get { return _sentence; } set { _sentence = value; RaisePropertyChanged("Sentence"); } }
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged(string propname)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propname));
}
}
使用IsSentenced
并将RadioButton绑定到它。另外,使用Visibility to Bool转换器检查显示Sentence
字符串的TextBox的可见性到RadioButton
的IsChecked属性。这是一个简单的例子:
<Window.Resources>
<local:VisibilityToBoolConverter x:Key="VisibilityToBoolConverter"/>
</Window.Resources>
<ListBox DataContext="{Binding}" ItemsSource="{Binding Persons}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBox Text="{Binding Name}" />
<RadioButton Content="Is sentenced to death" IsChecked="{Binding IsSentenced}" />
<DockPanel Visibility="{Binding IsSentenced , Converter={StaticResource VisibilityToBoolConverter}}">
<Label Content="Sentence: "/>
<TextBlock Text="{Binding Sentence}" />
</DockPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
其中
public class VisibilityToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((bool)value == true)
return Visibility.Visible;
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((Visibility)value == Visibility.Visible)
return true;
return false;
}
}
并且ViewModel是:
public class PersonViewModel : INotifyPropertyChanged
{
public PersonViewModel()
{
Person m1 = new Person() { Name = "person 1", IsSentenced = false, Sentence = "S S S" };
Person m2 = new Person() { Name = "person 2", IsSentenced = false, Sentence = "B B B" };
Person m3 = new Person() { Name = "person 3", IsSentenced = true, Sentence = "F F F" };
_persons = new ObservableCollection<Person>() { m1, m2, m3 };
}
ObservableCollection<Person> _persons;
public ObservableCollection<Person> Persons { get { return _persons; } set { _persons = value; RaisePropertyChanged("Persons"); } }
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged(string propname)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propname));
}
}
你的主窗口应该设置DataContext:
public MainWindow()
{
PersonViewModel mv = new PersonViewModel();
this.DataContext = mv;
InitializeComponent();
}
修改强>
如果一个人有很多状态,ComboBox是一个更自然的选择。你应该有一个描述状态的枚举:
public enum MyTypes
{
None,
IsA,
IsB,
IsC
}
并且Person应该有一个显示状态的peroperty:
public class Person : INotifyPropertyChanged
{
MyTypes _thetype;
public MyTypes TheType { get { return _thetype; } set { _thetype = value; RaisePropertyChanged("TheType"); } }
string _name;
public string Name { get { return _name; } set { _name = value; RaisePropertyChanged("Name"); } }
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged(string propname)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propname));
}
}
由于您需要将ComboBox的ItemsSource绑定到状态列表,因此可以调整ViewModel以获得这样的列表:
public class PersonViewModel : INotifyPropertyChanged
{
public PersonViewModel()
{
Person m0 = new Person() { Name = "person 1", TheType = MyTypes.None };
Person m1 = new Person() { Name = "person 1", TheType = MyTypes.IsA };
Person m2 = new Person() { Name = "person 2", TheType = MyTypes.IsB };
Person m3 = new Person() { Name = "person 3", TheType = MyTypes.IsC };
_persons = new ObservableCollection<Person>() { m0, m1, m2, m3 };
_types = Enum.GetNames(typeof(MyTypes)).ToList();
}
List<string> _types;
public List<string> Types { get { return _types; } }
ObservableCollection<Person> _persons;
public ObservableCollection<Person> Persons { get { return _persons; } set { _persons = value; RaisePropertyChanged("Persons"); } }
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged(string propname)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propname));
}
}
最后,观点:
<Window.Resources>
<local:EnumToSentenceConverterx:Key="EnumToSentenceConverter"/>
<local:NoneToCollapsedConverter x:Key="NoneToCollapsedConverter"/>
<local:EnumToStringConverter x:Key="EnumToStringConverter"/>
</Window.Resources>
<ListBox Name="lb" DataContext="{Binding}" ItemsSource="{Binding Persons}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBox Text="{Binding Name}" />
<ComboBox Name="cb" ItemsSource="{Binding ElementName=lb, Path=DataContext.Types}" SelectedValue="{Binding TheType, Mode=TwoWay, Converter={StaticResource EnumToStringConverter}}" />
<DockPanel Visibility="{Binding ElementName=cb, Path=SelectedValue, Converter={StaticResource NoneToCollapsedConverter}}">
<Label Content="Sentence: " DockPanel.Dock="Left"/>
<TextBlock Text="{Binding TheType, Converter={StaticResource EnumToStringConverter}}" DockPanel.Dock="Right" VerticalAlignment="Center" />
</DockPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
请注意,您需要三个转换器。一个将Sentence部分的Visibility设置为Collapsed,类型为None:
public class NoneToCollapsedConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value.ToString() == "None")
return Visibility.Collapsed;
return Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
另外两个是自我描述性的:
public class EnumToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value.ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Enum.Parse(typeof(MyTypes), value.ToString());
}
}
和
public class EnumToSentenceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
switch ((MyTypes)value)
{
case MyTypes.IsA:
break;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
希望它有所帮助。