繁体   English   中英

WPF自定义/用户控件,带有文本和图标的按钮。 我应该选择哪一个?

[英]WPF Custom/User Control for button with text and icon. Which one should I choose?

我想使用按钮的自定义控件来重构以下代码。

XAML文件: LoginWindow.xaml

<Button Grid.Column="0" Style="{DynamicResource BlackLoginButton}" Click="btnLogin_Click" IsDefault="True">
    <DockPanel>
        <TextBlock Text="text1" VerticalAlignment="Center"/>
        <Image Source="../../../Assets/Images/apply.png" Style="{StaticResource ImageButton}" />
    </DockPanel>
</Button>

背后的代码: LoginWindow.xaml.cs

private void btnLogin_Click(object sender, EventArgs e)
{
    Login();
}

我特别希望具有这样的结构:

<local:CustomButton>
    Grid.Column="0"
    IsDefault="True"
    Style="{DynamicResource BlackLoginButton}"
    Click="btnLogin_Click"
    Text="ciao" 
    Image="../../../Assets/Images/apply.png">
</local:CustomButton>

我尝试同时使用“自定义控件”和“用户控件”。

这是我的UserControl xaml

<UserControl x:Class="foo.View.CustomUserControl.IconButton"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <Button Style="{DynamicResource BlackLoginButton}">
        <DockPanel>
            <TextBlock VerticalAlignment="Center" Text="{Binding ElementName=Button, Path=Text}" />
            <Image Source="{Binding ElementName=Button, Path=Image}" Style="{StaticResource ImageButton}" />
        </DockPanel>
    </Button>
</UserControl>

以及后面的代码:

using System.Windows;
using System.Windows.Media;

namespace Mhira3D.View.CustomUserControl
{
    public partial class IconButton
    {
        public static DependencyProperty ClickProperty = DependencyProperty.Register("Click", typeof(RoutedEventHandler),
            typeof(IconButton));

        public static DependencyProperty ImageProperty = DependencyProperty.Register("Image", typeof(ImageSource),
            typeof(IconButton));

        public static DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(IconButton));

        public IconButton()
        {
            InitializeComponent();
        }

        public RoutedEventHandler Click
        {
            get { return (RoutedEventHandler) GetValue(ClickProperty); }
            set { SetValue(ClickProperty, value); }
        }

        public ImageSource Image
        {
            get { return (ImageSource) GetValue(ImageProperty); }
            set { SetValue(ImageProperty, value); }
        }

        public string Text
        {
            get { return this.GetValue(TextProperty) as string; }
            set { this.SetValue(TextProperty, value); }
        }
    }
}

这种结构的主要问题是我不能使用Button属性(我没有从button继承),例如我不能使用IsDefault

我认为这可以替代使用CustomControl ,以更好的方式使用button属性,例如(在此示例中,我仅放置Image属性):

public class IconButtonCustom : Button
{
    static IconButtonCustom()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(IconButtonCustom), new FrameworkPropertyMetadata(typeof(IconButtonCustom)));
    }
    public ImageSource Image
    {
        get { return GetValue(SourceProperty) as ImageSource; }
        set { SetValue(SourceProperty, value); }
    }
    public static readonly DependencyProperty SourceProperty =
      DependencyProperty.Register("Image", typeof(ImageSource), typeof(IconButtonCustom));
}

以及Generic.xaml的样式

<Style TargetType="{x:Type customUserControl:IconButtonCustom}" BasedOn="{StaticResource BlackLoginButton}">
        <Setter Property="Template">
            <Setter.Value>
            <ControlTemplate TargetType="{x:Type customUserControl:IconButtonCustom}">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                    <DockPanel>
                        <TextBlock VerticalAlignment="Center" Text="{Binding ElementName=Button, Path=Text}" />
                        <Image Source="{Binding ElementName=Button, Path=Image}" Style="{StaticResource ImageButton}" />
                    </DockPanel>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

现在我有一些问题:

  1. 以下哪种方法最适合创建我的自定义按钮?
  2. 使用这两种方法,在映射click函数时都会遇到问题,实际上我得到了System.Windows.Markup.XamlParseException因为WPF无法在函数Click转换字符串btnLogin_Click
  3. 我从未使用过Generic.xaml ,我的示例是正确的还是应该以某种方式进行改进?

好的,所以大多数Framework元素都有一个方便使用的属性,称为Tag ,该属性在诸如此类的实例中存在,在这种情况下,我们可以使用某种方式将某些内容附加到模板中,而不必声明其他依赖项属性等。

因此,如果我们采用默认的Button样式模板(右键单击button-> Edit Template-> Edit Copy),我们将在其中看到一个ContentPresenter ,它通常会传递任何CLR对象。 因此,对于您的文字或其他要求,我们非常有用。

现在,我们有多种选择来实现您的目标。 一种是简单地以这种方式(伪)传递您的两个元素。

<Button>
  <StackPanel Orientation="Horizontal">
     <TextBlock/>
     <Image/>
  </StackPanel>
</Button>

除了那似乎有点乏味。 因此,我们进入“ Style模板”选项,而是对它进行类似的操作;

<Style x:Key="SpecialButtonStyle" TargetType="{x:Type Button}">
    <!-- We set a default icon/image path for the instance one isn't defined. -->
    <Setter Property="Tag" Value="../../../Assets/Images/apply.png"/>
    <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
    <Setter Property="Background" Value="{StaticResource Button.Static.Background}"/>
    <Setter Property="BorderBrush" Value="{StaticResource Button.Static.Border}"/>
    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
    <Setter Property="BorderThickness" Value="1"/>
    <Setter Property="HorizontalContentAlignment" Value="Center"/>
    <Setter Property="VerticalContentAlignment" Value="Center"/>
    <Setter Property="Padding" Value="1"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Button}">
                <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto"/>
                            <ColumnDefinition Width="Auto"/>
                        </Grid.ColumnDefinitions>
                        <ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
             <!-- This is where we've added an Image to our Button with it's source bound to Tag. PS - In everything else like Silverlight, WP, UWP, Embedded etc, it's just {TemplateBinding Tag} -->
                        <Image Grid.Column="1" Source="{Binding Path=Tag, RelativeSource={RelativeSource TemplatedParent}}"/>
                    </Grid>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsDefaulted" Value="true">
                        <Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
                    </Trigger>
                    <Trigger Property="IsMouseOver" Value="true">
                        <Setter Property="Background" TargetName="border" Value="{StaticResource Button.MouseOver.Background}"/>
                        <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.MouseOver.Border}"/>
                    </Trigger>
                    <Trigger Property="IsPressed" Value="true">
                        <Setter Property="Background" TargetName="border" Value="{StaticResource Button.Pressed.Background}"/>
                        <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Pressed.Border}"/>
                    </Trigger>
                    <Trigger Property="IsEnabled" Value="false">
                        <Setter Property="Background" TargetName="border" Value="{StaticResource Button.Disabled.Background}"/>
                        <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Disabled.Border}"/>
                        <Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{StaticResource Button.Disabled.Foreground}"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

请注意,添加的Image元素的Source设置为{TemplateBinding Tag} ,顶部还有一个setter,它指定了默认图像的字符串路径。

因此,现在我们可以做;

<Button Content="Blah Blah Blah" Click="Click_Handler"
        Style="{StaticResource SpecialButtonStyle}"/>

...我们将获得一个带有默认图标的按钮和您的点击处理程序。 除了现在,我们需要为每个实例显示不同的图标。 因此,我们可以做类似的事情;

<Button Content="Blah Blah Blah" 
        Click="Click_Handler"
        Tag="../../../Assets/Images/DIFFERENTIMAGE.png"
        Style="{StaticResource SpecialButtonStyle}"/>

除此之外,似乎仍然有些令人生畏。 因此,我们可以更进一步,将您的资产字符串转化为资源。 将mscorlib命名空间添加为字典,例如`xmlns:sys“前缀,您可以执行此操作;

<sys:String x:Key="imageONE">../../../Assets/Images/imageONE.png</sys:String>
<sys:String x:Key="imageTWO">../../../Assets/Images/imageTWO.png</sys:String>
<sys:String x:Key="imageTHREE">../../../Assets/Images/imageTHREE.png</sys:String>

这给我们带来了一些好处。 如果您需要更改这些图标之一的文件路径/名称,一种更容易维护的方法。 您可以只在一个位置进行操作,它将继承到所有实例。 不必跟踪每个实例,这很困难。 它还允许我们在实例处将它们作为资源调用,因此现在我们的Button实例将如下所示;

<Button Content="Blah Blah Blah" 
        Click="Click_Handler"
        Tag="{StaticResource imageTWO}"
        Style="{StaticResource SpecialButtonStyle}"/>

瞧,您完成了。 但是,您应该考虑的另一个观察结果是文件路径。 我肯定会改用Pack Uri 只要您的路径正确并且对图像的构建操作也应该被设置。

希望这会有所帮助,欢呼。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM