WPF在单击按钮时更改按钮上的xaml图标

时间:2023-01-27 21:34:05

I am trying to create button that locks and unlocks a textbox the button displays a locked icon when the textbox is disabled and an unlocked lock when the textbox is enabled.

我正在尝试创建锁定和解锁文本框的按钮,当文本框被禁用时按钮显示锁定图标,启用文本框时显示解锁锁定。

I have done a bunch of reading and found the question on this site: WPF Change button background image when clicked

我做了一堆阅读并在这个网站上找到了问题:单击时WPF更改按钮背景图像

I took the answer and came up with this.

我接受了答案并提出了这个问题。

<Button Grid.Row="0" Command="{Binding ChangePnumTextState}" CommandParameter="{Binding ElementName=ButtonCanvas, Path=Source}">
    <Canvas Name="ButtonCanvas">
         <Canvas.Style>
              <Style TargetType="{x:Type Canvas}">
                   <Style.Triggers>
                        <DataTrigger Binding="{ Binding IsPNumLocked}" Value="True">
                            <Setter Property="Source" Value="{StaticResource appbar_lock}" />
                        </DataTrigger>
                        <DataTrigger Binding="{ Binding IsPNumLocked}" Value="False">
                            <Setter Property="Source" Value="{StaticResource appbar_unlock}" />
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
          </Canvas.Style>
     </Canvas>
</Button>

I have imported this batch of icons http://modernuiicons.com/ into xaml file that gets drawn when I need one

我已经将这批图标http://modernuiicons.com/导入到我需要的xaml文件中

I have looked around just cant find any examples of how to do this with SVG and not an image

我环顾四周,无法找到如何使用SVG而不是图像的任何示例

EDIT

Ok it was pointed out that SVG doesnt work with wpf I am probably just confusing names up heres an example of the code I have in a resource library that I am using to get the icons

好吧有人指出SVG不能与wpf一起使用我可能只是混淆了名称,这是我在资源库中使用的代码示例,我用来获取图标

<Canvas x:Key="appbar_3d_obj" Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">
    <Path Width="40" Height="40" Canvas.Left="18" Canvas.Top="18" Stretch="Fill" Fill="{DynamicResource BlackBrush}" Data="F1 M 18,21.7037L 43.9259,18L 58,25.4074L 58,54.2963L 32.8148,58L 18,49.1111L 18,21.7037 Z "/>
</Canvas>

The error I am getting is the visual studio underline the Property in the Setters and states

我得到的错误是视觉工作室强调了塞特犬和州的财产

"The member 'Source' is not recognized or is not accessible"

“会员'来源'无法识别或无法访问”

2 个解决方案

#1


3  

There's a few problems with the XAML you've got now.

你现在拥有的XAML存在一些问题。

As others have pointed out, there's no Source property on a WPF canvas. If all you're trying to do is make the button content canvases you've got defined already, you could use the same technique to bind the Content property of your Button to the Canvases defined in your App.xaml (or wherever it may be)

正如其他人指出的那样,WPF画布上没有Source属性。如果您要做的只是制作已经定义的按钮内容画布,您可以使用相同的技术将Button的Content属性绑定到App.xaml中定义的Canvases(或者它可能位于任何地方) )

Another issue you're likely running into is the binding within your data trigger. I haven't tested, but I presume you won't have the same datacontext within the style that you're expecting from your user control (or whatever contains the button) Doesn't look like the data context is an issue, I guess I was thinking of templating issues

您可能遇到的另一个问题是数据触发器中的绑定。我没有测试过,但我认为你在用户控件期望的样式中没有相同的datacontext(或包含按钮的任何东西)看起来不像数据上下文是一个问题,我想我在想模板问题

Also, I'm not sure what your ChangePnumTextState command is doing, but is there a reason you're sending in the canvas as the parameter? If your command is simply changing a bool value on IsPnumLocked, you should be able to handle everything in XAML and keep your separation of concerns.

另外,我不确定你的ChangePnumTextState命令在做什么,但你是否有理由在画布中作为参数发送?如果您的命令只是在IsPnumLocked上更改bool值,那么您应该能够处理XAML中的所有内容并保持关注点的分离。

Here's an easy change to make the content of the button the Canvas you have defined:

这是一个简单的更改,使按钮的内容为您定义的画布:

<Button Command="{Binding ChangePnumTextState}">
        <Button.Style>
            <Style TargetType="{x:Type Button}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding IsPNumLocked}" Value="True">
                        <Setter Property="Content" Value="{StaticResource appbar_lock}"/>
                    </DataTrigger>
                    <DataTrigger Binding="{Binding IsPNumLocked}" Value="False">
                        <Setter Property="Content" Value="{StaticResource appbar_unlock}"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Button.Style>
    </Button>

Notice how rather than trying to set the source of the canvas, we simply set the content of the button to the canvas you already have defined.

请注意,我们只是将按钮的内容设置为您已定义的画布,而不是尝试设置画布的来源。

Also the RelativeSource on the bindings should take care of the datacontext resolution (although you'll need to make sure that the relative source you search for has the datacontext you're after)

同样在绑定的RelativeSource应采取的datacontext分辨率的照顾(尽管你需要确保你搜索相对源有你后在DataContext)

Edit: Another approach would be to make a converter for your canvas paths based on a bool value (which I assume IsPNumLocked is). You could then just have a canvas as a child of the button's content, and it would be much easier to include other content as well.

编辑:另一种方法是根据bool值为我的​​画布路径制作转换器(我假设IsPNumLocked是)。然后,您可以将画布作为按钮内容的子画面,并且包含其他内容也会更容易。

Example converter:

    public class IsPnumLockedToCanvasPathConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        bool val = (bool)value;
        return val 
            ? "F1 M 22.17,36.4216L 25.3369,36.4216L 25.3369,31.6711C 25.3369,24.6745 31.0087,19.0027 38.0053,19.0027C 45.0019,19.0027 50.6737,24.6745 50.6737,31.6711L 50.6737,36.4216L 53.841,36.4216L 53.8411,57.008L 22.17,57.008L 22.17,36.4216 Z M 45.9231,31.6711C 45.9231,27.2982 42.3782,23.7533 38.0053,23.7533C 33.6324,23.7533 30.0875,27.2982 30.0875,31.6711L 30.0875,36.4216L 45.923,36.4216L 45.9231,31.6711 Z "
            : "F1 M 22.1698,36.4215L 25.3366,36.4215L 25.3367,31.6711C 25.3367,24.6745 31.0085,19.0027 38.0051,19.0027C 45.0017,19.0027 50.6735,24.6745 50.6735,31.6711L 45.9228,31.6711C 45.9228,27.2982 42.3779,23.7533 38.0051,23.7533C 33.6322,23.7533 30.0873,27.2982 30.0873,31.6711L 30.0873,36.4216L 53.8408,36.4215L 53.8409,57.008L 22.1698,57.008L 22.1698,36.4215 Z ";
    }

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

and your button could be set up like so:

你的按钮可以设置如下:

<Button Command="{Binding ChangePnumTextState}">
        <Button.Resources>
            <converters:IsPnumLockedToCanvasPathConverter x:Key="IsPnumLockedToCanvasPathConverter"/>
        </Button.Resources>
        <StackPanel Orientation="Vertical">
            <Canvas Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">
                <Path Width="40" Height="40" Canvas.Left="18" Canvas.Top="18" Stretch="Fill" Fill="{DynamicResource BlackBrush}" Data="{Binding IsPNumLocked, Converter={StaticResource IsPnumLockedToCanvasPathConverter}}"></Path>
            </Canvas>
            <TextBlock Text="Other Content"/>
        </StackPanel>

    </Button>

#2


1  

You are trying to set the Source property on a Canvas object. There is no Source property for that control.

您正在尝试在Canvas对象上设置Source属性。该控件没有Source属性。

The Source property is commonly used with an Image control.

Source属性通常与Image控件一起使用。

However, since you are using a Canvas, you can just add both of your Path objects to the Canvas, right on top of each other. Give each Path a name, and then use your DataTriggers to change the visibility of each Path object.

但是,由于您使用的是Canvas,因此您只需将两个Path对象添加到Canvas中,就可以将它们叠加在一起。为每个Path指定一个名称,然后使用DataTriggers更改每个Path对象的可见性。

Something like:

<Button Grid.Row="0" Command="{Binding ChangePnumTextState}" CommandParameter="{Binding ElementName=ButtonCanvas, Path=Source}">
<Canvas Name="ButtonCanvas">
     <Path Name="appbar_lock" Data="...your data here..."/>
     <Path Name="appbar_unlock" Data="...your data here..."/>
     <Canvas.Style>
          <Style TargetType="{x:Type Canvas}">
               <Style.Triggers>
                    <DataTrigger Binding="{Binding IsPNumLocked}" Value="True">
                        <Setter TargetName="appbar_lock" Property="Visibility" Value="Visible" />
                        <Setter TargetName="appbar_unlock" Property="Visibility" Value="Hidden" />
                    </DataTrigger>
                    <DataTrigger Binding="{Binding IsPNumLocked}" Value="False">
                        <Setter TargetName="appbar_lock" Property="Visibility" Value="Hidden" />
                        <Setter TargetName="appbar_unlock" Property="Visibility" Value="Visible" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
      </Canvas.Style>
 </Canvas>
</Button>

#1


3  

There's a few problems with the XAML you've got now.

你现在拥有的XAML存在一些问题。

As others have pointed out, there's no Source property on a WPF canvas. If all you're trying to do is make the button content canvases you've got defined already, you could use the same technique to bind the Content property of your Button to the Canvases defined in your App.xaml (or wherever it may be)

正如其他人指出的那样,WPF画布上没有Source属性。如果您要做的只是制作已经定义的按钮内容画布,您可以使用相同的技术将Button的Content属性绑定到App.xaml中定义的Canvases(或者它可能位于任何地方) )

Another issue you're likely running into is the binding within your data trigger. I haven't tested, but I presume you won't have the same datacontext within the style that you're expecting from your user control (or whatever contains the button) Doesn't look like the data context is an issue, I guess I was thinking of templating issues

您可能遇到的另一个问题是数据触发器中的绑定。我没有测试过,但我认为你在用户控件期望的样式中没有相同的datacontext(或包含按钮的任何东西)看起来不像数据上下文是一个问题,我想我在想模板问题

Also, I'm not sure what your ChangePnumTextState command is doing, but is there a reason you're sending in the canvas as the parameter? If your command is simply changing a bool value on IsPnumLocked, you should be able to handle everything in XAML and keep your separation of concerns.

另外,我不确定你的ChangePnumTextState命令在做什么,但你是否有理由在画布中作为参数发送?如果您的命令只是在IsPnumLocked上更改bool值,那么您应该能够处理XAML中的所有内容并保持关注点的分离。

Here's an easy change to make the content of the button the Canvas you have defined:

这是一个简单的更改,使按钮的内容为您定义的画布:

<Button Command="{Binding ChangePnumTextState}">
        <Button.Style>
            <Style TargetType="{x:Type Button}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding IsPNumLocked}" Value="True">
                        <Setter Property="Content" Value="{StaticResource appbar_lock}"/>
                    </DataTrigger>
                    <DataTrigger Binding="{Binding IsPNumLocked}" Value="False">
                        <Setter Property="Content" Value="{StaticResource appbar_unlock}"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Button.Style>
    </Button>

Notice how rather than trying to set the source of the canvas, we simply set the content of the button to the canvas you already have defined.

请注意,我们只是将按钮的内容设置为您已定义的画布,而不是尝试设置画布的来源。

Also the RelativeSource on the bindings should take care of the datacontext resolution (although you'll need to make sure that the relative source you search for has the datacontext you're after)

同样在绑定的RelativeSource应采取的datacontext分辨率的照顾(尽管你需要确保你搜索相对源有你后在DataContext)

Edit: Another approach would be to make a converter for your canvas paths based on a bool value (which I assume IsPNumLocked is). You could then just have a canvas as a child of the button's content, and it would be much easier to include other content as well.

编辑:另一种方法是根据bool值为我的​​画布路径制作转换器(我假设IsPNumLocked是)。然后,您可以将画布作为按钮内容的子画面,并且包含其他内容也会更容易。

Example converter:

    public class IsPnumLockedToCanvasPathConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        bool val = (bool)value;
        return val 
            ? "F1 M 22.17,36.4216L 25.3369,36.4216L 25.3369,31.6711C 25.3369,24.6745 31.0087,19.0027 38.0053,19.0027C 45.0019,19.0027 50.6737,24.6745 50.6737,31.6711L 50.6737,36.4216L 53.841,36.4216L 53.8411,57.008L 22.17,57.008L 22.17,36.4216 Z M 45.9231,31.6711C 45.9231,27.2982 42.3782,23.7533 38.0053,23.7533C 33.6324,23.7533 30.0875,27.2982 30.0875,31.6711L 30.0875,36.4216L 45.923,36.4216L 45.9231,31.6711 Z "
            : "F1 M 22.1698,36.4215L 25.3366,36.4215L 25.3367,31.6711C 25.3367,24.6745 31.0085,19.0027 38.0051,19.0027C 45.0017,19.0027 50.6735,24.6745 50.6735,31.6711L 45.9228,31.6711C 45.9228,27.2982 42.3779,23.7533 38.0051,23.7533C 33.6322,23.7533 30.0873,27.2982 30.0873,31.6711L 30.0873,36.4216L 53.8408,36.4215L 53.8409,57.008L 22.1698,57.008L 22.1698,36.4215 Z ";
    }

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

and your button could be set up like so:

你的按钮可以设置如下:

<Button Command="{Binding ChangePnumTextState}">
        <Button.Resources>
            <converters:IsPnumLockedToCanvasPathConverter x:Key="IsPnumLockedToCanvasPathConverter"/>
        </Button.Resources>
        <StackPanel Orientation="Vertical">
            <Canvas Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">
                <Path Width="40" Height="40" Canvas.Left="18" Canvas.Top="18" Stretch="Fill" Fill="{DynamicResource BlackBrush}" Data="{Binding IsPNumLocked, Converter={StaticResource IsPnumLockedToCanvasPathConverter}}"></Path>
            </Canvas>
            <TextBlock Text="Other Content"/>
        </StackPanel>

    </Button>

#2


1  

You are trying to set the Source property on a Canvas object. There is no Source property for that control.

您正在尝试在Canvas对象上设置Source属性。该控件没有Source属性。

The Source property is commonly used with an Image control.

Source属性通常与Image控件一起使用。

However, since you are using a Canvas, you can just add both of your Path objects to the Canvas, right on top of each other. Give each Path a name, and then use your DataTriggers to change the visibility of each Path object.

但是,由于您使用的是Canvas,因此您只需将两个Path对象添加到Canvas中,就可以将它们叠加在一起。为每个Path指定一个名称,然后使用DataTriggers更改每个Path对象的可见性。

Something like:

<Button Grid.Row="0" Command="{Binding ChangePnumTextState}" CommandParameter="{Binding ElementName=ButtonCanvas, Path=Source}">
<Canvas Name="ButtonCanvas">
     <Path Name="appbar_lock" Data="...your data here..."/>
     <Path Name="appbar_unlock" Data="...your data here..."/>
     <Canvas.Style>
          <Style TargetType="{x:Type Canvas}">
               <Style.Triggers>
                    <DataTrigger Binding="{Binding IsPNumLocked}" Value="True">
                        <Setter TargetName="appbar_lock" Property="Visibility" Value="Visible" />
                        <Setter TargetName="appbar_unlock" Property="Visibility" Value="Hidden" />
                    </DataTrigger>
                    <DataTrigger Binding="{Binding IsPNumLocked}" Value="False">
                        <Setter TargetName="appbar_lock" Property="Visibility" Value="Hidden" />
                        <Setter TargetName="appbar_unlock" Property="Visibility" Value="Visible" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
      </Canvas.Style>
 </Canvas>
</Button>