为什么自定义控件" ImageButton"没有显示它的图像?

时间:2016-05-10 12:30:37

标签: c# wpf xaml mahapps.metro

根据MahApps'我正在编写具有突出显示效果的图像按钮自定义控件。 AccentedSquareButtonStyle。的 ImageButton.xaml

<UserControl x:Class="NQR_GUI_WPF.ImageButton"
         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:NQR_GUI_WPF"
         mc:Ignorable="d" >
<Button Style="{StaticResource AccentedSquareButtonStyle}" Background="Transparent" Foreground="Transparent" BorderThickness="0" Width="24" Height="24" TouchDown="Button_TouchDown">
    <Grid Background="Transparent">
        <ContentControl>
            <ContentControl.Style>
                <Style TargetType="{x:Type ContentControl}">
                    <Setter Property="Content" Value="{Binding Image, RelativeSource={RelativeSource TemplatedParent}}"/>
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=IsMouseOver}" Value="True" >
                            <Setter Property="Content" Value="{Binding HighlightedImage, RelativeSource={RelativeSource TemplatedParent}}"/>
                        </DataTrigger>
                        <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=IsPressed}" Value="True" >
                            <Setter Property="Content" Value="{Binding ClickedImage, RelativeSource={RelativeSource TemplatedParent}}"/>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </ContentControl.Style>
        </ContentControl>
    </Grid>
</Button>

ImageButton.xaml.cs

namespace NQR_GUI_WPF
{
/// <summary>
/// Interaction logic for ImageButton.xaml
/// </summary>
public partial class ImageButton : UserControl
{
    public static DependencyProperty ImageProperty = DependencyProperty.Register("Image", typeof(Canvas), typeof(ImageButton));
    public static DependencyProperty ClickedImageProperty = DependencyProperty.Register("ClickedImage", typeof(Canvas), typeof(ImageButton));
    public static DependencyProperty HighlightedImageProperty = DependencyProperty.Register("HighlightedImage", typeof(Canvas), typeof(ImageButton));

    static ImageButton()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageButton), new FrameworkPropertyMetadata(typeof(ImageButton)));
    }

    public Canvas Image
    {
        get { return (Canvas)base.GetValue(ImageProperty); }
        set { base.SetValue(ImageProperty, value); }
    }

    public Canvas ClickedImage
    {
        get { return (Canvas)base.GetValue(ClickedImageProperty); }
        set { base.SetValue(ClickedImageProperty, value); }
    }

    public Canvas HighlightedImage
    {
        get { return (Canvas)base.GetValue(HighlightedImageProperty); }
        set { base.SetValue(HighlightedImageProperty, value); }
    }

    private void Button_TouchDown(object sender, TouchEventArgs e)
    {
        Keyboard.ClearFocus();
    }
}

}

示例图标:

<Canvas x:Key="printIcon" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Name="appbar_printer_text" Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">
            <Path Width="44" Height="45" Canvas.Left="16" Canvas.Top="17" Stretch="Fill" Fill="{Binding Source={x:Static prop:Settings.Default}, Path=theme, Converter={StaticResource idealForegroundConverter}}" Data="F1 M 25,27L 25,17L 51,17L 51,27L 47,27L 47,21L 29,21L 29,27L 25,27 Z M 16,28L 60,28L 60,51L 52,51L 52,46L 55,46L 55,33L 21,33L 21,46L 24,46L 24,51L 16,51L 16,28 Z M 25,39L 28,39L 28,52L 35,52L 35,59L 48,59L 48,39L 51,39L 51,62L 33,62L 25,54L 25,39 Z M 46,55L 38,55L 38,52L 46,52L 46,55 Z M 46,49L 30,49L 30,46L 46,46L 46,49 Z M 46,43L 30,43L 30,40L 46,40L 46,43 Z "/>
        </Canvas>

问题是在MainWindow中,添加存储在App.xaml中的图像后,控件为空(不显示图像)。

<local:ImageButton Image="{StaticResource printIcon}" HighlightedImage="{StaticResource printIconHighlighted}" ClickedImage="{StaticResource printIconClicked}" Grid.Column="1" HorizontalAlignment="Left" Height="46" Margin="36,10,0,0" VerticalAlignment="Top" Width="100"/>

我尝试将图像直接绑定到控件模板中,但没有成功(尽管在控件设计器视图中显示了图像)。 为什么没有显示控制图像?

4 个答案:

答案 0 :(得分:2)

UserControl不是您的最佳选择。 UserControls不适用于编写通用WPF控件。你可以做到,但这不是最简单的方法。最简单的方法是将常规控件(通常只是ContentControlHeaderedContentControl)子类化,然后为其编写样式和模板。一旦你掌握了这项技术,你就可以根据需要敲打它。通常,您可以为现有控件编写专用模板,但在您的情况下,您确实需要自己的Button子类。

我会将ImageButton写为Button的子类,其他依赖属性几乎与您定义它们相同,但我会将它们设为{{1}类型因此,消费者可以填充XAML可以呈现的任何内容。没有理由不给他们所有可以使用的绳子。我将使用Object属性而不是Content属性,因为这简化了事情。

如果由于某种原因您需要阻止非图片内容,您可以使用比Image更专业的内容类型,但您没有提及引入该限制的任何特定原因。

C#:

Object

XAML资源字典ImageButton.xaml:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace NQR_GUI_WPF
{
    /// <summary>
    /// Interaction logic for ImageButton.xaml
    /// </summary>
    public class ImageButton : Button
    {
        public ImageButton()
        {
            TouchDown += ImageButton_TouchDown;
        }

        private void ImageButton_TouchDown(object sender, TouchEventArgs e)
        {
            Keyboard.ClearFocus();
        }

        #region Dependency Properties
        public static DependencyProperty ClickedContentProperty = DependencyProperty.Register("ClickedContent", typeof(Object), typeof(ImageButton));
        public static DependencyProperty HighlightedContentProperty = DependencyProperty.Register("HighlightedContent", typeof(Object), typeof(ImageButton));

        public Object ClickedContent
        {
            get { return (Object)base.GetValue(ClickedContentProperty); }
            set { base.SetValue(ClickedContentProperty, value); }
        }

        public Object HighlightedContent
        {
            get { return (Object)base.GetValue(HighlightedContentProperty); }
            set { base.SetValue(HighlightedContentProperty, value); }
        }
        #endregion Dependency Properties
    }
}

以下是您的使用方式:

<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:nqrgui="clr-namespace:NQR_GUI_WPF"
    >

    <Style TargetType="{x:Type nqrgui:ImageButton}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type nqrgui:ImageButton}">
                    <Grid>
                        <ContentControl
                            Content="{TemplateBinding Content}"
                            x:Name="PART_Content"
                            />
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter 
                                TargetName="PART_Content" 
                                Property="Content" 
                                Value="{Binding HighlightedContent, RelativeSource={RelativeSource TemplatedParent}}" 
                                />
                        </Trigger>
                        <Trigger Property="IsPressed" Value="True">
                            <Setter 
                                TargetName="PART_Content" 
                                Property="Content" 
                                Value="{Binding ClickedContent, RelativeSource={RelativeSource TemplatedParent}}" 
                                />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

你真的可以绝对疯狂的内容:

<Window
    ...
    xmlns:nqrgui="clr-namespace:NQR_GUI_WPF"
    ...
    >

<!-- Or better yet, merge ImageButton.xaml in App.xaml so everybody can see it -->
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="ImageButton.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>

...

    <!-- As noted, Content, HighlightedContent, and ClickedContent 
    can be images -- or also paths, text, ANYTHING XAML can render.
    -->
    <nqrgui:ImageButton 
        Content="Content"
        HighlightedContent="Highlighted"
        ClickedContent="Clicked"
        />

答案 1 :(得分:0)

您错误地使用TemplateParent

而不是

{Binding Image, RelativeSource={RelativeSource TemplatedParent}}

它应该是这样的

{Binding RelativeSource={RelativeSource Mode=FindAncestor,
 AncestorType=ImageButton}, Path=Image}

答案 2 :(得分:0)

我在下面这样做了,

 <Controls:MetroWindow.Resources>
    <ImageBrush Stretch="Fill"  x:Key="CloseImage" ImageSource="../images/Close.png" />
    <ImageBrush x:Key="CloseImageRed" ImageSource="../images/CloseRed.jpg" />
  </Controls:MetroWindow.Resources>

<Button>
       <Button.Style>
         <Style TargetType="Button">
           <Setter Property="Background" Value="{StaticResource CloseImageRed}"/>
           <Setter Property="Template">
              <Setter.Value>
                  <ControlTemplate TargetType="{x:Type Button}">
                      <Border Background="{TemplateBinding Background}"
                              BorderBrush="{TemplateBinding BorderBrush}"
                              BorderThickness="{TemplateBinding BorderThickness}">
               <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                 Margin="{TemplateBinding Padding}" 
                                 VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                 SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                 RecognizesAccessKey="True"/>
                      </Border>
                     <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                           <Setter Property="Background" Value="{StaticResource CloseImage}"/> 
                         </Trigger>
                     </ControlTemplate.Triggers>
               </ControlTemplate>
           </Setter.Value>
       </Setter>                                                    
  </Style>
 </Button.Style>
</Button>

看看。

答案 3 :(得分:0)

您正在为自定义按钮设置UserControl.Content,我认为您要设置的是UserControl.ContentTemplate

.Content开始,没有&#34; TemplatedParent&#34;绑定到。但是,如果这是Template,那么TemplatedParent将指向为其定义模板的UserControl。在这种情况下,它会引用您的ImageButton UserControl,它可以正确地访问Image属性。

<UserControl ..>
    <UserControl.ContentTemplate>
        <ControlTemplate>
            <!-- TemplatedParent bindings should refer to UserControl from here -->
            <Button ... /> 
        </ControlTemplate>
    </UserControl.ContentTemplate>
</UserControl>

这也允许你写一些像

这样的东西
<local:ImageButton Content="Some Text" />

没有使用包含&#34; Text Text&#34;

的Text元素完全替换Button XAML代码

例如,您现在拥有的内容将呈现为

<UserControl>
    <Button /> <!-- Button is .Content, and can be replaced by XAML using the control -->
</UserControl>

如果它是ContentTemplate,它将呈现为

<UserControl>
    <Button> <!-- Button is ContentTemplate, so wraps any content given by external XAML -->
        <Content />
    </Button>
</UserControl>