Привязка данных Silverlight - привязка ValueConverter к свойству модели представления - PullRequest
2 голосов
/ 16 декабря 2008

Давайте представим, что у меня есть следующий xaml ...

<UserControl.Resources>
    <local:ViewModel x:Name="viewModel" />
    <local:LoadChildrenValueConverter x:Name="valueConverter" />
</UserControl.Resources>

<UserControl.DataContext>
    <Binding Source="{StaticResource viewModel}" />
</UserControl.DataContext>

<Grid x:Name="LayoutRoot" Background="White">
    <control:TreeView ItemsSource="{Binding Root}">
        <control:TreeView.ItemTemplate>
            <control:HierarchicalDataTemplate ItemsSource="{Binding Converter={StaticResource valueConverter}}">
                <TextBlock Text="{Binding}" />
            </control:HierarchicalDataTemplate>
        </control:TreeView.ItemTemplate>
    </control:TreeView>
</Grid>

... и следующий код, чтобы пойти с ним ...

using System;
using System.Collections.ObjectModel;
using System.Windows.Data;

namespace SilverlightViewModelSpike
{
    public class ViewModel
    {
        public ViewModel()
        {
            Root = new ObservableCollection() { "Item 1", "Item 2", "Item 3", };
        }

        public ObservableCollection Root { get; private set; }        
    }

    public class LoadChildrenValueConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return new ObservableCollection() { "Item 1", "Item 2", "Item 3", };
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

Это работает, как и ожидалось, но неправильно, что у меня есть два отдельных класса, которые требуются для того, чтобы получить необходимые данные для моего представления (представьте, что ViewModel и LoadChildrenValueConverter извлекают данные из веб-службы вместо возврата жестко закодированных данных) , Есть ли лучшее решение здесь? Я думал, может быть, что-то вроде этого ...

using System;
using System.Collections.ObjectModel;
using System.Windows.Data;

namespace SilverlightViewModelSpike
{
    public class ViewModel
    {
        public ViewModel()
        {
            Root = new ObservableCollection() { "Item 1", "Item 2", "Item 3", };
            ValueConverter = new LoadChildrenValueConverter();
        }

        public ObservableCollection Root { get; private set; }
        public LoadChildrenValueConverter ValueConverter { get; private set; }
    }

    public class LoadChildrenValueConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return new ObservableCollection() { "Item 1", "Item 2", "Item 3", };
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

... но тогда я не могу заставить эту строку работать ...

<control:HierarchicalDataTemplate ItemsSource="{???}">

... и даже это не кажется отличным решением. У кого-нибудь есть хорошее чистое решение для этого?

Ответы [ 2 ]

4 голосов
/ 16 декабря 2008

Поскольку вы используете ViewModel, чтобы сидеть между вашей фактической моделью и вашим представлением, мне интересно, проще ли просто реализовать логику IValueConverter прямо там. Вроде как:

public class ViewModel
{
    public ObservableCollection Root { get; set: }

    public ObservableCollection Children
    {
        get { /* return children items */ }
    }
}

Тогда вы можете просто связать непосредственно со вторым свойством:

<control:HierarchicalDataTemplate ItemsSource="{Binding Children}">

Я думаю, что основная цель объекта ViewModel - ограничить количество «уловок» (таких как IValueConverters), которые вам нужно получить, чтобы получить необходимые данные из исходной модели. Так как у вас есть, вы можете использовать его.

Редактировать 1

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

Как насчет реализации IValueConverter как статического экземпляра в самой вашей ViewModel?

public class ViewModel : IValueConverter
{ 
    public static readonly IValueConverter ChildrenConverter
        = new LoadChildrenValueConverter();
}

Теперь вы должны быть в состоянии сказать:

<control:HierarchicalDataTemplate 
    ItemsSource="{Binding Converter={x:Static local:ViewModel.ChildrenConverter}}">

Редактировать 2

Хорошо, вы используете Silverlight, поэтому {x: Static} вам недоступен.

Единственная другая опция, о которой я могу подумать, позволит вам повторно использовать один статический ресурс вместо того, чтобы объявлять два, - это реализовать IValueConverter непосредственно в вашей ViewModel. Это бесполезно, если вам нужно выполнить более одного типа преобразования, но если ваша ViewModel очень узко сфокусирована, то она может выполнить эту работу. Итак:

public class ViewModel : IValueConverter
{
    // move your Convert and ConvertBack methods into here
}

Теперь вы можете сделать это:

<control:HierarchicalDataTemplate
    ItemsSource="{Binding Converter={StaticResource ViewModel}}">
0 голосов
/ 09 июня 2011

Извините, ребята, я немного озадачен тем, что вы пытаетесь сделать здесь ... В любом случае, от названия это звучит так, как будто вы хотите, чтобы свойство в вашем преобразователе значений было свойством в вашем преобразователе значений. Во-первых, посмотрите на статью, которую я написал, объясняющую, как именно вы можете это сделать: http://nick -howard.blogspot.com / 2011/06/4-Silverlight-значение-converter.html

Итак, вам нужно создать свойство зависимостей ObvervableCollection в вашем LoadChildrenValueConverter, чтобы аргументы называли его Children.

Тогда в вашем xaml вы можете изменить LoadChildrenValueConverter на что-то вроде этого:

Таким образом, вы вызываете веб-сервис только один раз из вашей модели представления, а затем делитесь этой ObvervableCollection в вашей модели представления с конвертером значений.

Надеюсь, это поможет.

...