簡體   English   中英

為什么自定義控件“ ImageButton”不顯示其圖像?

[英]Why does a custom control “ImageButton” not display it's image?

我正在基於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"/>

我嘗試將圖像直接綁定到控件模板中,但是沒有成功(盡管在控件設計器視圖中顯示了圖像)。 為什么不顯示控制圖像?

UserControl不是您的最佳選擇。 UserControls並非用於編寫通用WPF控件。 您可以做到,但這不是最簡單的方法。 最簡單的方法是對常規控件(通常只是ContentControlHeaderedContentControl )進行子類化,然后HeaderedContentControl編寫樣式和模板。 一旦確定了這項技術,就可以根據需要將其刪除。 通常,您可以為現有控件編寫專門的模板,但是在這種情況下,您確實需要自己的Button子類。

我會將ImageButton編寫為Button的子類,並具有幾乎與您定義它們一樣的其他依賴項屬性,但是我將它們設置為Object類型,以便使用者可以在其中填充XAML可以呈現的任何內容。 沒有理由不給他們所有可以使用的繩索。 而且我將使用Content屬性而不是Image屬性,因為這簡化了事情。

如果出於某種原因您需要防止非圖像內容,則可以使用比Object更專業的內容類型,但是您沒有提及引入此限制的任何特殊原因。

C#:

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
    }
}

XAML資源字典ImageButton.xaml:

<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"
        />

而且您真的可以對內容絕對發瘋:

    <!-- Don't try this in a UI anybody will have to use! -->
    <nqrgui:ImageButton 
        Content="Content"
        ClickedContent="Clicked"
        >
        <nqrgui:ImageButton.HighlightedContent>
            <StackPanel Orientation="Horizontal">
                <Border 
                    BorderBrush="Gray" 
                    Background="GhostWhite" 
                    BorderThickness="1">
                    <Path 
                        Width="20" 
                        Height="20" 
                        Stroke="Black" 
                        StrokeThickness="2"
                        Data="M 0,0 L 20,20 M 0,20 L 20,0"
                        Margin="2"
                        />
                </Border>
                <nqrgui:ImageButton
                    Content="LOL"
                    ClickedContent="Don't Click Me, Bro!"
                    HighlightedContent="I heard you like buttons"
                    />
            </StackPanel>
        </nqrgui:ImageButton.HighlightedContent>
    </nqrgui:ImageButton>

您使用的TemplateParent錯誤

代替這個

{Binding Image, RelativeSource={RelativeSource TemplatedParent}}

應該是這樣的

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

我在下面做了這樣的事情,

 <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>

看一看。

您正在將UserControl.Content設置為自定義的Button,我想您要設置的是UserControl.ContentTemplate

.Content內部,沒有要綁定的“ TemplatedParent”。 但是,如果這是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" />

而不用包含“某些文本”的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>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM