Использование ресурса и DataTemplateSelector из другой сборки - PullRequest
0 голосов
/ 18 сентября 2011

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

Для этого я пытаюсь использовать ресурс и подкласс DataTemplateSelector в вызывающей сборке, как показано ниже. Первоначально я надеялся, что будет вызван подклассифицированный DataTemplateSelector, но это не так. Затем я попытался добавить запись в Словарь ресурсов вызывающей сборки с тем же ключом, но с субклассовым селектором, но это также не делает этого.

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

Приветствия
Berryl

Пользовательский контроль (базовая сборка)

<UserControl 
    ...

    <Grid>
        <Border Style="{StaticResource FilterPanelBorderStyle}">
            <StackPanel Orientation="Horizontal" x:Name="myFilterPanel" >

      *****     <ContentControl x:Name="ctrlFilters" ContentTemplateSelector="{StaticResource filterControlsTemplateSelector}" /> ****

                <Button x:Name="btnClearFilter" Style="{StaticResource FilterPanelClearButtonStyle}" />
                <Label x:Name="lblStatus" Style="{StaticResource FilterPanelLabelStyle}" Content="{Binding Status}" />

            </StackPanel>
        </Border>

    </Grid>
</UserControl>

Resources и DataTemplateSelector (базовая сборка)

<views:FilterControlsTemplateSelector x:Key="filterControlsTemplateSelector"/>

<DataTemplate x:Key="defaultFilterContent">
    <TextBlock>Replace ME with real filters!</TextBlock>
</DataTemplate>

    public class FilterControlsTemplateSelector : DataTemplateSelector
{
    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        var fe = container as FrameworkElement;
        if (fe == null) return null;

        return _GetDataTemplate(fe);
    }

    protected virtual DataTemplate _GetDataTemplate(FrameworkElement fe) {
        var template = fe.FindResource("defaultFilterContent") as DataTemplate;
        return template;
    }
}

Ресурсы и селектор (вызывающая сборка)

<ResourceDictionary.MergedDictionaries>
    <ResourceDictionary Source="pack://application:,,,/Core.Presentation.Wpf;component/Themes/Generic.xaml" />
</ResourceDictionary.MergedDictionaries>

<local:PimFilterControlsTemplateSelector x:Key="filterControlsTemplateSelector"/>

<DataTemplate x:Key="pimFilterContent">
    <Grid>
        <Border Style="{StaticResource FilterPanelBorderStyle}">
            <StackPanel Orientation="Horizontal" >
                <cc:SearchTextBox 
                        x:Name="stbLastNameFilter" Style="{StaticResource FilterPanelSearchTextBoxStyle}"
                        />
                <cc:SearchTextBox 
                        x:Name="stbFirstNameFilter" Style="{StaticResource FilterPanelSearchTextBoxStyle}"
                        />
            </StackPanel>
        </Border>

    </Grid>
</DataTemplate>


public class PimFilterControlsTemplateSelector : FilterControlsTemplateSelector
{

    protected override DataTemplate _GetDataTemplate(FrameworkElement fe)
    {
        var dc = fe.DataContext;
        if (dc == null) return null;

        DataTemplate result = null;
        if (dc is PimMasterVm)
        {
            result = fe.FindResource("pimFilterContent") as DataTemplate;
        }
        else {
            result = base._GetDataTemplate(fe);
        }
        return result;
    }

}

Настройка словаря приложения (вызывающая сборка)

    <Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="pack://application:,,,/Parties.Presentation.Wpf;component/PimCommonResources.xaml" />                
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>

</Application.Resources>

1 Ответ

0 голосов
/ 21 сентября 2011

Я отказался от работы DataTemplateSelector и завелся следующим образом:

  1. создание FilterContentConverter
  2. добавление FilterContentKey (string) в мою модель просмотра

Преобразователь просто берет FilterContentKey и выполняет поиск ресурса, чтобы получить шаблон данных с этим ключом. Получается, что он хорошо тестируется, а еще лучше - работает!

Код решения ниже, с благодарностью Владамиру Дорохову и этому SO-ответу за помощь в получении правильной привязки ContentControl.

НТН,
Berryl

Контроль фильтрации

enter image description here

<Grid>
    <Border Style="{StaticResource FilterPanelBorderStyle}">
        <StackPanel Orientation="Horizontal" x:Name="myFilterPanel" >
            <ContentControl x:Name="ctrlFilters" 
                            ContentTemplate="{Binding Path=FilterContentKey, Converter={StaticResource filterTemplateContentConv}}" />
            <Button x:Name="btnClearFilter" Style="{StaticResource FilterPanelClearButtonStyle}" />
            <Label x:Name="lblStatus" Style="{StaticResource FilterPanelLabelStyle}" Content="{Binding Status}" />

        </StackPanel>
    </Border>

</Grid>

Шаблон данных (ресурс)

<DataTemplate x:Key="pimFilterContent">
    <StackPanel Orientation="Horizontal" >
        <cc:SearchTextBox x:Name="stbLastNameFilter" 
            Style="{StaticResource FilterPanelSearchTextBoxStyle}"
            Text="{Binding Path=LastNameFilter, UpdateSourceTrigger=PropertyChanged}" 
                        />
        <cc:SearchTextBox x:Name="stbFirstNameFilter" 
            Style="{StaticResource FilterPanelSearchTextBoxStyle}"
            Text="{Binding Path=FirstNameFilter, UpdateSourceTrigger=PropertyChanged}" 
                        />
    </StackPanel>
</DataTemplate>

Преобразователь

/// <summary>
/// Thin wrapper around a resource lookup designed to result in a <see cref="DataTemplate"/> 
/// representing filering controls.
/// </summary>
[ValueConversion(typeof(object), typeof(DataTemplate))]
public class FilterTemplateContentConverter : IValueConverter
{
    public const string DEFAULT_CONTENT = "undefinedFilterContent";
    protected readonly ResourceLocator _resourceLocator;

    /// <summary>
    /// Initializes a new instance of the <see cref="FilterTemplateContentConverter"/> class.
    /// Unit tests can use this to pass in an app for the <see cref="ResourceLocator"/>.
    /// </summary>
    /// <param name="app">The app.</param>
    public FilterTemplateContentConverter(Application app) { _resourceLocator = new ResourceLocator(app); }

    /// <summary>
    /// Initializes a new instance of the <see cref="FilterTemplateContentConverter"/> class.
    /// The 'real' application uses this.
    /// </summary>
    public FilterTemplateContentConverter()
    {
        _resourceLocator = new ResourceLocator();
    }

    public virtual object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
        var key = value as string;
        return _resourceLocator.GetResource(key ?? DEFAULT_CONTENT);
    }

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