使用来自不同程序集的资源和 DataTemplateSelector

发布于 2024-12-05 02:49:22 字数 3692 浏览 3 评论 0原文

我在基础程序集中有一个控件,其中包含我想根据当前 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>

资源和 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>

I have a control in a base assembly with Content that I would like to set based on the current DataContext.

To do so, I am trying to use a resource and subclassed DataTemplateSelector in the calling assembly as shown below. My initial hope was that the sub classed DataTemplateSelector would be called, but it isn't. Then I tried adding an entry in the Resource Dictionary of the calling assembly with the same key but the sub classed selector, but that doesn't get it done either.

Is there a way to fix the code I have to make this work? Is there a better strategy to set my content from the calling assembly?

Cheers,
Berryl

User Control (base assembly)

<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 and DataTemplateSelector (base assembly)

<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;
    }
}

Resources and Selector (calling Assembly)

<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 Dictionary setup (calling assembly)

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

</Application.Resources>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

薄荷港 2024-12-12 02:49:23

我放弃了让 DataTemplateSelector 工作并最终执行以下操作:

  1. 创建 FilterContentConverter
  2. 将 FilterContentKey (字符串)添加到我的视图模型

该转换器仅获取 FilterContentKey 并执行资源查找以获取具有该键的 DataTemplate。这最终可以很好地进行测试,甚至更好 - 它有效!

下面的解决方案代码,感谢 Vladamir Dorokhov 和这个 SO 答案帮助我正确绑定 ContentControl。

HTH,
Berryl

过滤控件

在此处输入图像描述

<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(); }
}

I gave up on making the DataTemplateSelector work and wound up doing the following:

  1. creating a FilterContentConverter
  2. adding a FilterContentKey (string) to my view model

The converter just takes the FilterContentKey and does a resource lookup to get the DataTemplate with that key. This winds up being nicely testable, and even better - it works!

Solution code below, with thanks to Vladamir Dorokhov and this SO answer for helping me get the ContentControl binding right.

HTH,
Berryl

Filtering Control

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>

Data Template (resource)

<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>

Converter

/// <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(); }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文