Привязка данных на IValueConverter - PullRequest
7 голосов
/ 09 марта 2009

Кто-нибудь знает, возможно ли связывание данных в классе на основе IValueConverter?

У меня есть следующий конвертер:

[ValueConversion(typeof(int), typeof(Article))]
public class LookupArticleConverter : FrameworkElement, IValueConverter {
    public static readonly DependencyProperty ArticlesProperty = DependencyProperty.Register("Articles", typeof(IEnumerable<Article>), typeof(LookupArticleConverter)); 

    public IEnumerable<Article> Articles
    {
        get { return (IEnumerable<Article>)GetValue(ArticlesProperty); }
        set { SetValue(ArticlesProperty, value); }
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
        ...
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
        ...
    }
}

Его цель - найти статью в списке по ее идентификатору и вернуть эту статью.

Однако я хотел бы заполнить свойство Articles, привязав к нему коллекцию, например:

<local:LookupArticleConverter Articles="{Binding Articles}" x:Key="LookupArticle"/>

Но, похоже, это не работает. Метод установки никогда не вызывается. Свойство source содержит фактическую непустую коллекцию, так что это не проблема.

Также нет сообщений об ошибках связывания в выходном журнале.

Есть какие-нибудь подсказки?

Ответы [ 3 ]

10 голосов
/ 18 декабря 2011

Итак, проблема в том, что ресурсы не являются частью визуального дерева. Для этого вам необходимо:

1. Сделайте так, чтобы ваш ValueConverter наследовал Freezable

 public class CustomConverter : Freezable, IValueConverter
 {

    public static readonly DependencyProperty LookupItemsProperty =
        DependencyProperty.Register("LookupItems", typeof(IEnumerable<LookupItem>), typeof(CustomConverter), new PropertyMetadata(default(IEnumerable<LookupItem>)));

    public IEnumerable<LookupItem> LookupItems
    {
        get { return (IEnumerable<LookupItem>)GetValue(LookupItemsProperty); }
        set { SetValue(LookupItemsProperty, value); }
    }

    #region Overrides of Freezable

    /// <summary>
    /// When implemented in a derived class, creates a new instance of the <see cref="T:System.Windows.Freezable"/> derived class.
    /// </summary>
    /// <returns>
    /// The new instance.
    /// </returns>
    protected override Freezable CreateInstanceCore()
    {
        return new CustomConverter();
    }

    #endregion Overrides of Freezable

    // ... Usual IValueConverter stuff ...

}

2. Привязка к визуальному дереву с помощью Binding ElementName

<UserControl (...) x:Name=myUserControl> 
<UserControl.Resources>
<CustomConverter x:Key="customConverter"
                    LookupItems="{Binding ElementName=myUserControl, Path=DataContext.LookupItems}"/>
</UserControl.Resources>
3 голосов
/ 09 марта 2009

WPF действительно не поддерживает это по своей природе. Тем не менее, есть несколько методов, которые позволят вам сделать это, включая подход виртуальной ветви Джоша Смита .

0 голосов
/ 05 января 2012

Я добавил метод RelativeSource в оператор связывания. Это решает проблему, в которой мой IValueConverter определен в дереве Control.Resources.ResourceDictionary, и я пытаюсь получить DataContext из Control.DataContext

<UserControl.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="/Resources/Text.xaml"/>
            <ResourceDictionary Source="/Resources/Layout.xaml"/>
            <ResourceDictionary Source="/Resources/Colours.xaml"/>
            <ResourceDictionary Source="/Resources/Icons.xaml"/>
        </ResourceDictionary.MergedDictionaries>

        <conv:OrientationConverter x:Key="OerientationConverter" IsOrientationRuleEnabled="{Binding Path=DataContext.IsSiteLayoutPhysical, RelativeSource={RelativeSource AncestorType={x:Type UserControl}, Mode=FindAncestor}}"/>
....
<ListView x:Name="operationsListView" ItemsSource="{Binding .}" DataContext="{Binding GroupedPlantItems}" ItemsPanel="{DynamicResource ItemsPanelTemplate1}" Panel.ZIndex="-10">
            <ListView.GroupStyle>
                <GroupStyle>
                    <GroupStyle.Panel >
                        <ItemsPanelTemplate>
                            <StackPanel Orientation="{Binding Name, Converter={StaticResource OerientationConverter}}"/>
                        </ItemsPanelTemplate>
                    </GroupStyle.Panel>
                    <GroupStyle.ContainerStyle>
                        <Style TargetType="{x:Type GroupItem}">
                            <Setter Property="Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="{x:Type GroupItem}">
                                        <GroupBox Header="{Binding Name}">
                                            <ItemsPresenter/>
                                        </GroupBox>
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </GroupStyle.ContainerStyle>
                </GroupStyle>
            </ListView.GroupStyle>
        </ListView>

Я тут немного хитрый и меняю способ, которым StackPanel ориентирует свое содержимое на основе некоторых правил, определенных в IValueConverter. Я использую привязку на конвертере, чтобы отключить или включить правила из моей ViewModel. Я мог бы также установить правила этим методом или, возможно, передать его лямбда-выражение для проверки. прошу прощения за мои опечатки.

...