WPF XAML 资源对象仅可见一次

Posted

技术标签:

【中文标题】WPF XAML 资源对象仅可见一次【英文标题】:WPF XAML Resource Object Is Only Visible Once 【发布时间】:2021-10-31 00:57:47 【问题描述】:

尝试了几个小时都没有成功,很想知道为什么三个按钮中只有一个显示图标 ViewBox 内容。

我觉得我对控件模板的内部工作了解不够。

第一个文件 ButtonProperties 类是包含 Icon ViewBox 的附加属性的所有者。 第二个文件是我的 MainWindow.xaml 窗口,它应该显示 3 个带有 Viewbox 图标的按钮。但只有最后一个按钮实例显示 Viewbox 图标。

感谢您的帮助。

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;

namespace WpfApp1

    public class ButtonProperties : DependencyObject
     

        public static readonly DependencyProperty IconViewboxProperty = DependencyProperty.RegisterAttached("IconViewbox",
            typeof(Viewbox), typeof(ButtonProperties), new PropertyMetadata(null));

        public static void SetIconViewbox(DependencyObject obj, Viewbox viewbox)
        
            obj.SetValue(IconViewboxProperty, viewbox);
        

        public static Viewbox GetIconViewbox(DependencyObject obj)
        
            return (Viewbox) obj.GetValue(IconViewboxProperty);
        
    


MainWindow.xaml

<Window
    x:Class="WpfApp1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:WpfApp1"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="MainWindow"
    Width="800"
    Height="450"
    mc:Ignorable="d">
    <Window.Resources>
        <ResourceDictionary>
            <Viewbox x:Key="IconTrash">
                <Canvas Width="24" Height="24">
                    <Path Data="M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z" Fill="Binding Path=Foreground, RelativeSource=RelativeSource AncestorType=Button" />
                </Canvas>
            </Viewbox>

            <Style x:Key="ButtonIcon" TargetType="Button">
                <Setter Property="Background" Value="Transparent" />
                <Setter Property="Foreground" Value="Black" />
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="Button">
                            <Border Background="TemplateBinding Background">
                                <ContentControl x:Name="IconControl" Content="Binding Path=(local:ButtonProperties.IconViewbox), RelativeSource=RelativeSource TemplatedParent" />
                            </Border>
                            <ControlTemplate.Triggers>
                                <Trigger Property="IsMouseOver" Value="True">
                                    <Setter TargetName="IconControl" Property="Opacity" Value="0.5" />
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>

            <Style
                x:Key="ButtonIconTrash"
                BasedOn="StaticResource ButtonIcon"
                TargetType="Button">
                <Setter Property="local:ButtonProperties.IconViewbox" Value="DynamicResource IconTrash" />
            </Style>
        </ResourceDictionary>
    </Window.Resources>

    <StackPanel Orientation="Vertical">
        <Button
            Width="25"
            Height="25"
            Style="DynamicResource ButtonIconTrash" />
        <Button
            Width="25"
            Height="25"
            Style="DynamicResource ButtonIconTrash" />
        <Button
            Width="25"
            Height="25"
            Style="DynamicResource ButtonIconTrash" />
    </StackPanel>
</Window>



【问题讨论】:

【参考方案1】:

默认情况下所有资源都是共享的。通常 XAML 处理器会创建一个已定义资源的实例,例如 DataTemplate 并重用它。但是,如果将资源直接添加到对象图中,并且由于默认情况下 XAML 处理器始终创建资源的单个实例并在整个范围内共享它,则该实例只能成为 XAML 对象图的一部分。 换句话说,直接添加到对象图(可视树或FrameworkElement 的情况下也是逻辑树)的Visual 实例不能同时在此图中的多个位置。

您必须通过将元素上的x:Shared 属性设置为false 来显式禁用共享。这会强制 XAML 处理器为对象图中的每个事件创建一个新的资源实例:

<Viewbox x:Key="IconTrash" x:Shared="False">
  <Canvas Width="24" Height="24">
    <Path Data="M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z" Fill="Binding Path=Foreground, RelativeSource=RelativeSource AncestorType=Button" />
  </Canvas>
</Viewbox>

【讨论】:

非常感谢,救了我剩下的头发;) 好好照顾他们。至少为严重的问题节省几缕头发。一般来说,建议在头上的某个地方抓一把。

以上是关于WPF XAML 资源对象仅可见一次的主要内容,如果未能解决你的问题,请参考以下文章

XAML 在多个选项之间切换可见性

WPF App.xaml.cs常用模板,包括:异常捕获,App只能启动一次

WPF--XAML资源

WPF--XAML资源

WPF--XAML资源

关于 WPF 中资源字典的问题