在控件中使用XAML资源不是从FrameworkElement继承的

时间:2014-03-08 18:54:46

标签: c# wpf xaml

我有一个自定义控件,我需要从GridView继承。 标准方式,适用于UserControl,

<UserControl.Resources>
    <local:NodeToImageConverter x:Key="imageConverter" />
    <local:ImageHeightConverter x:Key="imageHeightConverter" />
</UserControl.Resources>

它对我不起作用。 我真的想使用本地资源 - 不要把它们放得更高。 我尝试创建一个级别的类并为那里的资源声明一个依赖属性。但它不起作用:

public partial class CustomDetailsView2 : GridViewRes {
    public CustomDetailsView2() {
        InitializeComponent();
    } // it is interesting that if i set a breakpoint here i see my resourses - 2 items
}
public class GridViewRes : GridView {
    public static DependencyProperty ResourcesProperty = DependencyProperty.Register(
        "Resources",
        typeof(ResourceDictionary),
        typeof(CustomDetailsView2),
        new FrameworkPropertyMetadata() {
            DefaultValue = new ResourceDictionary()
        });
    public ResourceDictionary Resources {
        get { return (ResourceDictionary)GetValue(ResourcesProperty); }
        set { SetValue(ResourcesProperty, value); }
    }
}

这是xaml

<local:GridViewRes x:Class="Nexplorer.UIParts.CustomDetailsView2"
         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" 
         xmlns:local="clr-namespace:Nexplorer.UIParts"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">

<local:GridViewRes.Resources>
    <local:NodeToImageConverter x:Key="imageConverter" />
    <local:ImageHeightConverter x:Key="imageHeightConverter" />
</local:GridViewRes.Resources>

i tried also attached property - with no success
<local:CustomProperties.Resources>
    <local:NodeToImageConverter x:Key="imageConverter" />
    <local:ImageHeightConverter x:Key="imageHeightConverter" />
</local:CustomProperties.Resources>

<GridViewColumn Header="Name">
    <GridViewColumn.CellTemplate>
        <DataTemplate>
            <Grid  HorizontalAlignment="Stretch">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition />
                </Grid.ColumnDefinitions>

                <Image Grid.Column="0" Stretch="Fill" HorizontalAlignment="Left">
                    <Image.Source>
                        <MultiBinding Converter="{StaticResource imageConverter}"> here i have a runtime "imageConverter resourse not found" exception...
                            <Binding Path="IconSize" ElementName="thisControl"/>
                            <Binding />
                        </MultiBinding>
                    </Image.Source>
                </Image>
                <TextBlock Grid.Column="1" Text="{Binding Path=DisplayName}" VerticalAlignment="Center" Margin="2 1 0 1"/>

                <Grid.Height>
                    <Binding Path="IconSize" Converter="{StaticResource imageHeightConverter}" ElementName="thisControl" />
                </Grid.Height>
            </Grid>
        </DataTemplate>
    </GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Size">
    <GridViewColumn.CellTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Path=DisplaySize}" />
        </DataTemplate>
    </GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Modified">
    <GridViewColumn.CellTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Path=DisplayModifiedDateTime}" />
        </DataTemplate>
    </GridViewColumn.CellTemplate>
</GridViewColumn>

</local:GridViewRes>

如何在此xaml中声明资源?

2 个答案:

答案 0 :(得分:4)

确定。删除所有代码并从头开始。

如果你正在使用WPF,你需要在古代UI框架中留下过去可能使用的所有传统方法,并理解并接受The WPF Mentality

基本上,除非您想要创建特定于控件的功能,否则永远不会继承WPF UI元素(或控件)。使用Styles and Templates在WPF中实现更改任何UI元素(或其子元素)的可视外观。

这是你在WPF中的表现方式:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <DataTemplate x:Key="TilesTemplate">
            <ListView ItemsSource="{Binding Files}">
                <ListView.Template>
                    <ControlTemplate TargetType="ListView">
                        <WrapPanel IsItemsHost="True" ItemWidth="200"/>
                    </ControlTemplate>
                </ListView.Template>

                <ListView.ItemTemplate>
                    <DataTemplate>
                        <DockPanel>
                            <Image DockPanel.Dock="Left" Height="32" Width="32" Margin="5"
                                   Source="http://files.softicons.com/download/internet-icons/bremen-icons-by-pc.de/png/32/folder.png"/>

                            <StackPanel>
                                <TextBlock Text="{Binding Name}" FontWeight="Bold" TextTrimming="CharacterEllipsis"/>
                                <TextBlock Text="{Binding Length}" Foreground="Gray"/>
                            </StackPanel>
                        </DockPanel>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </DataTemplate>

        <DataTemplate x:Key="DetailTemplate">
            <ListView ItemsSource="{Binding Files}">
                <ListView.View>
                    <GridView>
                        <GridViewColumn Header="File Name" DisplayMemberBinding="{Binding Name}"/>
                        <GridViewColumn Header="Size" DisplayMemberBinding="{Binding Length}"/>
                    </GridView>
                </ListView.View>
            </ListView>
        </DataTemplate>

    </Window.Resources>

    <DockPanel>
        <StackPanel Orientation="Horizontal" DockPanel.Dock="Top">
            <RadioButton Content="Tiles" IsChecked="{Binding TilesMode}"/>
            <RadioButton Content="Details" IsChecked="{Binding DetailsMode}"/>
        </StackPanel>

        <ContentControl Content="{Binding}">
            <ContentControl.Style>
                <Style TargetType="ContentControl">
                    <Style.Triggers>
                        <!-- This DataTrigger will change the View Mode to Tiles -->
                        <DataTrigger Binding="{Binding TilesMode}" Value="True">
                            <Setter Property="ContentTemplate" Value="{StaticResource TilesTemplate}"/>
                        </DataTrigger>

                        <!-- This DataTrigger will change the View Mode to Details -->
                        <DataTrigger Binding="{Binding DetailsMode}" Value="True">
                            <Setter Property="ContentTemplate" Value="{StaticResource DetailTemplate}"/>
                        </DataTrigger>

                    </Style.Triggers>
                </Style>
            </ContentControl.Style>
        </ContentControl>
    </DockPanel>
</Window>

代码背后:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        DataContext = new ExplorerViewModel();
    }
}

视图模型:

public class ExplorerViewModel: INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }

    private bool _detailsMode;
    public bool DetailsMode
    {
        get { return _detailsMode; }
        set 
        {
            _detailsMode = value;
            OnPropertyChanged();
        }
    }

    private bool _tilesMode;
    public bool TilesMode
    {
        get { return _tilesMode; }
        set
        {
            _tilesMode = value;
            OnPropertyChanged();
        }
    }

    public ObservableCollection<FileInfo> Files { get; set; } 

    public ExplorerViewModel()
    {
        var path = @"C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE";

        Files = new ObservableCollection<FileInfo>(Directory.GetFiles(path).Select(x => new FileInfo(x)));

        TilesMode = true;
    }
}

结果:

enter image description here

  • 请注意我如何使用ListView之类的标准WPF UI元素以及上述链接中所述的DataTemplate等概念。

  • 请注意,这种方法非常简洁,因为它需要更少的代码,并且通过使用正确的DataBinding使UI与数据完全分离。

  • 与传统的UI框架(例如winforms)相比,您无法自定义任何内容,并且您不得不使用默认内容,WPF采用了不同的方法。它没有太多“开箱即用”的UI,因为可定制性水平很高,您可以在任何时间轻松创建100%自定义UI(如此处所示)。

  • 运行此示例时,请确保path构造函数中的ExplorerViewModel变量指向硬盘驱动器上的有效路径。

  • WPF Rocks。 - 只需将我的代码复制并粘贴到File -> New Project -> WPF Application中,然后自行查看结果。

答案 1 :(得分:0)

实际上有一种简单的方法可以做到这一点。这是所有自动生成的UserControls和Windows使用的范例,但它不仅限于它们,你甚至可以在子类化时使用它。

看一下如何在代码和XAML中定义stock MainWindow类 - 在创建新的WPF应用程序项目时为您自动生成的类。它在XAML中使用基类型(你会发现它以<Window x:Class="WpfApplication1.MainWindow">而不是它的派生类型MainWindow开头。同样适用于你的GridView,只需使用它的基本类型,而不是本地:GridViewRes。然后,您可以使用其所有继承的属性,包括Resources,以及其他任何地方。唯一的限制是您不能将代码隐藏XAML中的值分配给您在派生类中新定义的属性,但我几乎无法想想一个有用的场景 - 你也可以在代码中做到这一点(当然,任何使用你的控件的人都可以在XAML中完成它,包括从GridView继承的属性)。

当然,正如已经说过的那样,应该仔细考虑他真的是否需要子类化WPF控件。它带来了一些问题,通常更清晰的解决方案是将其包装到UserControl中,并仅导出您想要与世界共享的少数属性。只是我的两分钱。