WPF 绑定到枚举
我正在尝试学习 WPF 中的一些数据绑定,因此已开始构建一个小型条件生成器测试应用程序,但在弄清楚我的绑定时遇到了一些问题。
所以我有一组基于泛型的条件。这是带有 BooleanCondition 的简单示例的类结构。
public abstract class AbstractCondition
{
/// <summary>
/// Gets the entry mode.
/// </summary>
/// <value>The entry mode.</value>
public abstract InputFieldMode InputFieldMode { get; }
}
public abstract class GenericCondition<T, O> : AbstractCondition
{
/// <summary>
/// Checks the specified value.
/// </summary>
/// <param name="value">The value.</param>
/// <returns></returns>
public abstract Boolean Check(T value);
/// <summary>
/// Gets or sets the value.
/// </summary>
/// <value>The value.</value>
public object Value
{
get;
set;
}
/// <summary>
/// Gets or sets the options.
/// </summary>
/// <value>The options.</value>
public O Option
{
get;
set;
}
/// <summary>
/// Gets the available options.
/// </summary>
/// <value>The available options.</value>
public abstract ObservableCollection<O> AvaliableOptions { get; }
}
/// <summary>
/// Describes a Condition that operations on a Boolean
/// </summary>
/// <typeparam name="U"></typeparam>
public class BooleanCondition : GenericCondition<Boolean?, BooleanConditionOption>
{
/// <summary>
/// Initializes a new instance of the <see cref="BooleanCondition<U>"/> class.
/// </summary>
/// <param name="option">The option.</param>
public BooleanCondition(BooleanConditionOption option)
{
this.Option = option;
}
/// <summary>
/// Checks the specified value.
/// </summary>
/// <param name="value">The value.</param>
/// <returns></returns>
public override bool Check(bool? value)
{
if (value.HasValue)
{
switch (this.Option)
{
case BooleanConditionOption.IsFalse:
return !value.Value;
case BooleanConditionOption.IsTrue:
return value.Value;
}
}
return false;
}
/// <inheritdoc />
public override InputFieldMode InputFieldMode
{
get { return InputFieldMode.NoField; }
}
/// <inheritdoc />
public override ObservableCollection<BooleanConditionOption> AvaliableOptions
{
get
{
var options = Enum.GetValues(typeof(BooleanConditionOption));
var optionsCollection = new ObservableCollection<BooleanConditionOption>(options.Cast<BooleanConditionOption>());
return optionsCollection;
}
}
}
目前有 4 个条件,其背后的基本思想是条件对一种对象类型进行操作,并提供一组特定于该类型的“ConditionOptions”(Bool 有 isTrue、isFalse,而数字可能有 < ; <= > >= 等)。
所以现在我正在尝试创建一个视图,以使条件更容易在 UI 上使用。使用从具有数据类型的组合中选择的值来调用更新。此时,我构造了适当的条件,并希望通过 ConditionValues 属性公开 ConditionOptions 集合(因此是枚举的结果,但它可以是 4 个中的任何一个)。
public class ConditionView
{
/// <summary>
/// Gets the Fields that are available for selection.
/// </summary>
public ObservableCollection<IDataField> Fields { get; set; }
public ObservableCollection<Object> ConditionValues { get; set; }
/// <summary>
/// Gets the Condition that has been selected.
/// </summary>
public AbstractCondition SelectedCondition { get; private set; }
/// <summary>
/// Update various options based upon the Selection
/// </summary>
/// <param name="field">The IDataField selected</param>
public void Update(IDataField field)
{
if (field != null)
{
// Determine what sort of condition we need
switch (field.Type)
{
case DataType.Boolean:
BooleanCondition booleanCondition = new BooleanCondition(BooleanConditionOption.IsFalse);
this.SelectedCondition = booleanCondition;
this.ConditionValues = booleanCondition.AvaliableOptions;
break;
case DataType.String:
this.SelectedCondition = new StringCondition(StringConditionOption.Contains);
break;
case DataType.Numeric:
this.SelectedCondition = new NumericCondition(NumericConditionOption.Equals);
break;
case DataType.Date:
this.SelectedCondition = new DateCondition(DateConditionOption.Equals);
break;
default:
throw new InvalidOperationException("Unknown Data Type");
}
}
}
我目前只是想让布尔值继续运行,但我不确定 ObservableCollection 是否是我应该绑定的对象(尝试时出现构建错误:“错误 5 无法隐式转换类型 'System.Collections.ObjectModel. ObservableCollection' 到 'System.Collections.ObjectModel.ObservableCollection')。我应该如何处理这个绑定问题?最终我有一些转换器也将在枚举上工作,所以我不确定将结果转换为对象是正确的:S
更新:
尝试将“字段”绑定到组合框,一旦它发生变化,对更新的调用将修改绑定到另一个组合框的“条件值”。
<UserControl x:Class="ConditionBuilder.Views.Condition"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
xmlns:cb="clr-namespace:ConditionBuilder"
xmlns:wm="clr-namespace:Watermark"
>
<UserControl.Resources>
<cb:EnumToUIConvertor x:Key="enumItemsConverter"/>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="229"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<ComboBox x:Name="cmbField" Grid.Column="0" Height="22" Width="120" ItemsSource="{Binding Path=Fields}" SelectionChanged="cmbField_SelectionChanged">
<ComboBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Path=Name, Mode=OneWay}" Height="Auto" Margin="-5" VerticalAlignment="Stretch"/>
</DataTemplate>
</ComboBox.ItemTemplate>
<wm:Watermark.WatermarkContent>
<Label Padding="0">Select Field...</Label>
</wm:Watermark.WatermarkContent>
</ComboBox>
<ComboBox x:Name="cmbCondition" Grid.Column="1" Height="22" Width="120" ItemsSource="{Binding Path=ConditionValues, Mode=OneWay}">
<wm:Watermark.WatermarkContent>
<Label Padding="0">Select Condition...</Label>
</wm:Watermark.WatermarkContent>
</ComboBox>
</Grid>
</UserControl>
I'm trying to learn some data-binding in WPF, so have started building a little condition builder test application, but am having some trouble figuring out my binding.
So I have a set of conditions that are based upon generics. This is the class structure with just a simple example of a BooleanCondition.
public abstract class AbstractCondition
{
/// <summary>
/// Gets the entry mode.
/// </summary>
/// <value>The entry mode.</value>
public abstract InputFieldMode InputFieldMode { get; }
}
public abstract class GenericCondition<T, O> : AbstractCondition
{
/// <summary>
/// Checks the specified value.
/// </summary>
/// <param name="value">The value.</param>
/// <returns></returns>
public abstract Boolean Check(T value);
/// <summary>
/// Gets or sets the value.
/// </summary>
/// <value>The value.</value>
public object Value
{
get;
set;
}
/// <summary>
/// Gets or sets the options.
/// </summary>
/// <value>The options.</value>
public O Option
{
get;
set;
}
/// <summary>
/// Gets the available options.
/// </summary>
/// <value>The available options.</value>
public abstract ObservableCollection<O> AvaliableOptions { get; }
}
/// <summary>
/// Describes a Condition that operations on a Boolean
/// </summary>
/// <typeparam name="U"></typeparam>
public class BooleanCondition : GenericCondition<Boolean?, BooleanConditionOption>
{
/// <summary>
/// Initializes a new instance of the <see cref="BooleanCondition<U>"/> class.
/// </summary>
/// <param name="option">The option.</param>
public BooleanCondition(BooleanConditionOption option)
{
this.Option = option;
}
/// <summary>
/// Checks the specified value.
/// </summary>
/// <param name="value">The value.</param>
/// <returns></returns>
public override bool Check(bool? value)
{
if (value.HasValue)
{
switch (this.Option)
{
case BooleanConditionOption.IsFalse:
return !value.Value;
case BooleanConditionOption.IsTrue:
return value.Value;
}
}
return false;
}
/// <inheritdoc />
public override InputFieldMode InputFieldMode
{
get { return InputFieldMode.NoField; }
}
/// <inheritdoc />
public override ObservableCollection<BooleanConditionOption> AvaliableOptions
{
get
{
var options = Enum.GetValues(typeof(BooleanConditionOption));
var optionsCollection = new ObservableCollection<BooleanConditionOption>(options.Cast<BooleanConditionOption>());
return optionsCollection;
}
}
}
There are 4 Conditions currently, the basic idea behind this, is that a Condition operates on a type of Object, and provides a set of 'ConditionOptions' that are particular for that type (Bool has isTrue, isFalse, whereas a number might have < <= > >= etc).
So now I'm trying to create a view to make the Condition easier to consume on the UI. An Update is called with a selected value from a combo, that has a datatype. At that point I construct the appropriate condition, and want to expose a collection of ConditionOptions via the ConditionValues property (so the results of an enum, but it could be anyone of 4).
public class ConditionView
{
/// <summary>
/// Gets the Fields that are available for selection.
/// </summary>
public ObservableCollection<IDataField> Fields { get; set; }
public ObservableCollection<Object> ConditionValues { get; set; }
/// <summary>
/// Gets the Condition that has been selected.
/// </summary>
public AbstractCondition SelectedCondition { get; private set; }
/// <summary>
/// Update various options based upon the Selection
/// </summary>
/// <param name="field">The IDataField selected</param>
public void Update(IDataField field)
{
if (field != null)
{
// Determine what sort of condition we need
switch (field.Type)
{
case DataType.Boolean:
BooleanCondition booleanCondition = new BooleanCondition(BooleanConditionOption.IsFalse);
this.SelectedCondition = booleanCondition;
this.ConditionValues = booleanCondition.AvaliableOptions;
break;
case DataType.String:
this.SelectedCondition = new StringCondition(StringConditionOption.Contains);
break;
case DataType.Numeric:
this.SelectedCondition = new NumericCondition(NumericConditionOption.Equals);
break;
case DataType.Date:
this.SelectedCondition = new DateCondition(DateConditionOption.Equals);
break;
default:
throw new InvalidOperationException("Unknown Data Type");
}
}
}
I'm currently just trying to get the Boolean one going, but I'm not sure if ObservableCollection is what I should be binding to (I get a build error trying: "Error 5 Cannot implicitly convert type 'System.Collections.ObjectModel.ObservableCollection' to 'System.Collections.ObjectModel.ObservableCollection'). How should I be approaching this binding issue? Ultimately I've got some Convertors that are going to expect to be working on an enum too, so I'm not sure just casting the results to Objects is right :S
Update:
Trying to bind 'Fields' to a ComboBox, and once it varies a call through to Update will modify 'ConditionValues' which is bound to another ComboBox.
<UserControl x:Class="ConditionBuilder.Views.Condition"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
xmlns:cb="clr-namespace:ConditionBuilder"
xmlns:wm="clr-namespace:Watermark"
>
<UserControl.Resources>
<cb:EnumToUIConvertor x:Key="enumItemsConverter"/>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="229"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<ComboBox x:Name="cmbField" Grid.Column="0" Height="22" Width="120" ItemsSource="{Binding Path=Fields}" SelectionChanged="cmbField_SelectionChanged">
<ComboBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Path=Name, Mode=OneWay}" Height="Auto" Margin="-5" VerticalAlignment="Stretch"/>
</DataTemplate>
</ComboBox.ItemTemplate>
<wm:Watermark.WatermarkContent>
<Label Padding="0">Select Field...</Label>
</wm:Watermark.WatermarkContent>
</ComboBox>
<ComboBox x:Name="cmbCondition" Grid.Column="1" Height="22" Width="120" ItemsSource="{Binding Path=ConditionValues, Mode=OneWay}">
<wm:Watermark.WatermarkContent>
<Label Padding="0">Select Condition...</Label>
</wm:Watermark.WatermarkContent>
</ComboBox>
</Grid>
</UserControl>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我想我现在明白了。您可能可以绑定到
SelectedCondition.AvailableOptions
并完全取消ConditionValues
。但是,我在您的代码中没有看到任何INotifyPropertyChanged
,因此除非您使用 notifypropertyweaver< /a> 或其他一些技巧,您可能需要使SelectedCondition
引发属性更改事件。I think I understand now. You could probably bind to
SelectedCondition.AvailableOptions
and do away withConditionValues
altogether. However, I don't see anyINotifyPropertyChanged
in your code so unless you're using notifypropertyweaver or some other trick, you will probably need to makeSelectedCondition
raise a property changed event.