UserControl DataBinding с преобразованием данных - PullRequest
0 голосов
/ 23 февраля 2012

Я относительно новичок в WPF и хотел бы создать UserControl, который содержит три радиобокса в StackPanel. Каждая из переключателей имеет свойство IsChecked. Я хочу облегчить доступ к этому выбору, опубликовав целочисленное свойство, которое указывает текущий выбор, назовите его SelectedIndex.

Таким образом, когда проверяется первое радио, значение SelectedIndex моего пользовательского элемента управления должно быть 0; когда проверяется второе радио, SelectedIndex должен быть равен 1 и т. д. Вероятно, это должно работать в любом направлении: когда пользователь щелкает переключатель, значение SelectedIndex должно соответственно изменяться. А когда значение SelectedIndex изменяется кодом или, возможно, даже какой-либо привязкой данных, элементы управления пользовательского интерфейса должны обновляться соответствующим образом.

Как я могу сделать это с кодом 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>

Я знаю, как передавать значения (например, один из текста RadioButton) из свойства моего UserControl. Но на этот раз мне нужно сопоставить «внешнее» значение int с тремя «внутренними» значениями bool, каждое из которых имеет значение true, когда число имеет определенное значение. Я предполагаю, что это должно быть сделано непосредственно в XAML, но я не могу понять, как этого добиться.

Или лучше написать код C # для этого преобразования? Я мог бы сделать это сейчас, но я все еще не уверен насчет специального кода DependencyProperty, который, я думаю, может изменить свое значение, не передавая мой установщик.

Ответы [ 3 ]

0 голосов
/ 23 февраля 2012

Наиболее распространенное решение - не пытаться использовать числовое представление, из которого выбрано RadioButton, потому что вы идете против течения, если попытаетесь.

Однако, чтобы попытаться ответить на вопрос, тем не менее...

Я не думаю, что попытался бы решить эту проблему в XAML, потому что это не играет на сильных сторонах XAML.Люди, которые относительно недавно знакомы с WPF, часто пытаются сделать все возможное в XAML, независимо от того, имеет ли это смысл или нет, потому что они откуда-то пришли к выводу, что это то, что вы должны делать.

I 'Я не говорю, что это невозможно.Чтобы ты не думал, что я пытаюсь отговорить тебя от этого, потому что я не знаю, как это сделать в XAML, я покажу точно, как я думаю, что ты не должен этого делать.Это работает для небольших значений «works»:

<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, чтобы обновить значение свойства зависимостей.Не просто используйте сеттер, потому что это испортит любые привязки данных или другое специфическое для DP использование вашего свойства и, по существу, лишит смысла превращать его в DP.

Кроме того,Я бы не назвал это SelectedIndex, потому что это имя свойства, которое обычно означает что-то конкретное в WPF - оно в основном используется производными от ItemsControl элементами управления, которые поддерживают выбор.Это не то, что вы делаете здесь, поэтому я бы предпочел выбрать имя, которое обычно не имеет смысла.За исключением того, что я уже сказал, я не думаю, что я бы использовал числовой индекс в любом случае.

0 голосов
/ 23 февраля 2012

Используйте конвертер значений следующим образом:

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, всегда будет устанавливаться истинное значение, поэтому вы никогда не должны возвращать ноль. Я не знаю, как это могло произойти, по крайней мере.

Затем в своем 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 в другом месте, за ним будут следовать кнопки RadioButton.

Кроме того, я забыл упомянуть ранее, но этот код предполагает, что вы сами установили DataContext вашего CommandControl, то есть в конструкторе:

    public CommandControl()
    {
        InitializeComponent();
        DataContext = this;
        SelectedIndex = 1;
    }
0 голосов
/ 23 февраля 2012

Просто используйте Boolean strait up, а не integer = 0 или integer = 1. Откройте публичное логическое свойство, например, rbDisbleIsChecked и привязать IsChecked к этому свойству. Если вы действительно хотите использовать Integer для открытого свойства, вам нужно будет использовать конвертер, так как IsChecked - Boolean.

Если вы хотите, чтобы целое число представляло текущий выбранный, вы можете установить это значение в наборе логического открытого свойства.

Или вы не можете связать и просто подключить все к тем же или отдельным проверенным событиям. Установите SelectedIndex в событии. Если вы подключите их все к одному и тому же событию, вам нужно будет присвоить им имена, и вызов события определит, который был проверен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...