UserControl DataBinding与数据转换

时间:2012-02-23 15:37:54

标签: .net wpf data-binding user-controls

我是WPF的新手,想在StackPanel中创建一个包含三个RadioBox的UserControl。每个单选按钮都有一个IsChecked属性。我希望通过发布指示当前选择的整数属性来简化对该选择的访问,将其称为SelectedIndex。

因此,当检查第一个无线电时,我的用户控制的SelectedIndex值应为0;当选中第二个无线电时,SelectedIndex应为1,依此类推。这应该可以在任何一个方向上工作:当用户点击单选框时,SelectedIndex值应相应地改变。当通过代码或甚至某些数据绑定更改SelectedIndex值时,UI控件将相应地更新。

如何使用XAML代码执行此操作?我现在有以下代码:

public partial class CommandControl : UserControl
{
    public static readonly DependencyProperty SelectedIndexProperty =
        DependencyProperty.Register("SelectedIndex", typeof(int), typeof(CommandControl));

    public int SelectedIndex
    {
        get
        {
            return (int) GetValue(SelectedIndexProperty);
        }
        set
        {
            SetValue(SelectedIndexProperty, value);
        }
    }

    public CommandControl()
    {
        InitializeComponent();
    }
}

XAML部分:

<UserControl x:Class="MyNamespace.CommandControl"
             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" 
             d:DesignHeight="65" d:DesignWidth="219"
             x:Name="me">
    <StackPanel HorizontalAlignment="Left" Name="stackPanel1" VerticalAlignment="Center" Orientation="Horizontal">
        <RadioButton Content="Disable" Name="disableRadio" VerticalAlignment="Center" IsChecked="True" />
        <RadioButton Content="Enable" Name="enableRadio" VerticalAlignment="Center" Margin="5,0,0,0" />
        <RadioButton Content="Configure" Name="configureRadio" VerticalAlignment="Center" Margin="5,0,0,0" />
    </StackPanel>
</UserControl>

我知道如何从UserControl的属性传递值(例如RadioButton的Text之一)。但这一次,我需要将“外部”int值映射到三个“内部”bool值,当数字具有特定值时,每个值都为true。我想这应该直接在XAML中完成,但我无法弄清楚如何实现这一点。

或者为该转换编写C#代码会更好吗?我现在可能会这样做,但我仍然不确定特殊的DependencyProperty代码,我想,它可能会在不通过我的setter的情况下更改其值。

3 个答案:

答案 0 :(得分:0)

只使用布尔strait up而不是integer = 0或integer = 1.公开一个Public Boolean属性,例如rbDisbleIsChecked并将IsChecked绑定到该属性。如果你真的想将Integer用于公共属性,那么你将需要使用转换器,因为IsChecked是布尔值。

如果希望Integer表示当前选定的当前值,则可以在布尔公共属性的集合中设置该值。

或者你无法绑定,只是连接到相同或单独的已检查事件。在事件中设置SelectedIndex。如果你将它们连接到同一个事件,那么你需要命名它们,因此事件调用确定检查了哪个。

答案 1 :(得分:0)

最常见的解决方案是不要尝试使用选择了RadioButton的数字表示,因为如果你尝试的话,你会反对这个流程。

然而,尝试回答这个问题......

我认为我不会尝试在XAML中解决这个问题,因为这不符合XAML的优势。 WPF相对较新的人通常会尝试在XAML中尽一切可能,无论它是否有意义,因为他们从某个地方获得了你应该做的事情。

我不是说这是不可能的。以免你认为我试图说服你,因为我不知道如何在XAML中做到这一点,我会准确地说明我认为你不应该这样做。这适用于“作品”的小值:

<UserControl
    x:Class="MyNamespace.CommandControl"
    xmlns:loc="clr-namespace:MyNamespace"
    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"  
    d:DesignHeight="65" d:DesignWidth="219" 
x:Name="me">

    <StackPanel HorizontalAlignment="Left" Name="stackPanel1" VerticalAlignment="Center" Orientation="Horizontal">
        <RadioButton Content="Disable" Name="disableRadio" VerticalAlignment="Center" IsChecked="True" />
        <RadioButton Content="Enable" Name="enableRadio" VerticalAlignment="Center" Margin="5,0,0,0" />
        <RadioButton Content="Configure" Name="configureRadio" VerticalAlignment="Center" Margin="5,0,0,0" />
    </StackPanel>

    <UserControl.Style>
        <Style TargetType="loc:MyNamespace">
            <Style.Triggers>
                <DataTrigger Binding="{Binding ElementName=disableRadio, Path=IsChecked}" Value="True">
                    <DataTrigger.Setters>
                        <Setter Property="SelectedIndex" Value="0" />
                    </DataTrigger.Setters>
                </DataTrigger>
                <DataTrigger Binding="{Binding ElementName=enableRadio, Path=IsChecked}" Value="True">
                    <DataTrigger.Setters>
                        <Setter Property="SelectedIndex" Value="1" />
                    </DataTrigger.Setters>
                </DataTrigger>
                <DataTrigger Binding="{Binding ElementName=configureRadio, Path=IsChecked}" Value="True">
                    <DataTrigger.Setters>
                        <Setter Property="SelectedIndex" Value="2" />
                    </DataTrigger.Setters>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </UserControl.Style>
</UserControl>

但这非常狡猾。首先,Visual Studio将假装出现错误 - 只要您打开此Xaml文件,它就会报告错误“'CommandControl'TargetType与元素类型'UserControl'不匹配。”其次,只有将Style放在用户控件的主体后面才有效。坦率地说,这些问题都是代码味道 - 这可能是你可能做错事的线索。在这种情况下,你会是。

顺便说一句,Visaul Studio没有用错误信息说出真相 - 这不是编译时错误,程序将构建并运行,并使用以下主窗口内容:

<Grid>
    <my:CommandControl HorizontalAlignment="Left" Margin="224,114,0,0" x:Name="commandControl1" VerticalAlignment="Top" />
    <TextBlock Height="82" HorizontalAlignment="Left" Margin="76,162,0,0" Name="textBlock1" Text="{Binding ElementName=commandControl1, Path=SelectedIndex}" VerticalAlignment="Top" Width="237" />
</Grid>

我已经确认这会正确更新该属性。

但这都错了。

首先,您在这里尝试创建的是编程功能 - 您正在确定控件的API行为方式。这远远超出了XAML的优势。哎呀,即使在XAML中嵌入交互行为也是值得怀疑的。我的观点是XAML主要用于应用程序的布局和外观。

所以代码隐藏真的是你正在做的事情的地方。只需使用良好的老式事件处理。请务必使用SetCurrentValue更新您的depenedency属性的值。不要只使用setter,因为这会破坏你的属性的任何数据绑定或其他DP特定用途,并且基本上会破坏使它成为DP的重点。

此外,我不会将其称为SelectedIndex,因为这是一个属性名称,通常意味着WPF中的特定内容 - 它主要由支持选择的ItemsControl派生控件使用。那不是你在这里所做的,所以我倾向于选择一个通常没有意义的名字。除非我已经说过,我认为在任何情况下我都不会使用数字索引。

答案 2 :(得分:0)

使用这样的值转换器:

public class RadioValueConverter : IValueConverter
{
    public int Value { get; set; }

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if ((int)value == Value)
        {
            return true;
        }
        return false;

    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if ((bool)value == true)
        {
            return Value;
        }
        return null;
    }
}

在ConvertBack中,单击RadioButton将始终设置一个true值,因此您永远不应该返回null。我不知道至少会发生这种情况。

然后在您的XAML中,您可以为每个RadioButton创建一个转换器:

<StackPanel HorizontalAlignment="Left" Name="stackPanel1" VerticalAlignment="Center" Orientation="Horizontal">
    <StackPanel.Resources>
        <loc:RadioValueConverter x:Key="FirstValue" Value="1"/>
        <loc:RadioValueConverter x:Key="SecondValue" Value="2"/>
        <loc:RadioValueConverter x:Key="ThirdValue" Value="3"/>
    </StackPanel.Resources>
    <RadioButton Content="Disable" Name="disableRadio" VerticalAlignment="Center" IsChecked="{Binding SelectedIndex, Converter={StaticResource FirstValue}}" />
    <RadioButton Content="Enable" Name="enableRadio" VerticalAlignment="Center" Margin="5,0,0,0" IsChecked="{Binding SelectedIndex, Converter={StaticResource SecondValue}}" />
    <RadioButton Content="Configure" Name="configureRadio" VerticalAlignment="Center" Margin="5,0,0,0" IsChecked="{Binding SelectedIndex, Converter={StaticResource ThirdValue}}"/>
</StackPanel>

这也应该提供双向绑定,所以如果你在其他地方更改SelectedIndex,RadioButtons将会跟随。

另外,我之前忘了提到但是这段代码假设你已经将CommandControl的DataContext设置为自身,即在构造函数中:

    public CommandControl()
    {
        InitializeComponent();
        DataContext = this;
        SelectedIndex = 1;
    }