2016-05-02 15 views
1

Ich möchte den folgenden Code mit einem benutzerdefinierten Steuerelement für die Schaltfläche umgestalten.WPF Custom/User Control für Schaltfläche mit Text und Symbol. Welches soll ich nehmen?

XAML-Datei: 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> 

-Code hinter: LoginWindow.xaml.cs

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

ich besonders würde ich eine Struktur wie diese haben mag:

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

Ich versuche zu verwenden sowohl Custom Control als auch User Control.

Hier mein UserControl XAML

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

Und der Code hinter:

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

Das Hauptproblem dieser Struktur ist, dass ich nicht Button Eigenschaft verwenden kann (ich von Taste nicht erben), zum Beispiel kann ich IsDefault nicht verwenden.

ich denke, es könnte eine Alternative Gebrauch sein ein CustomControl auf die Schaltfläche Eigenschaft in einer besseren Art und Weise zu verwenden, wie folgt aus (in thi Beispiel, das ich nur setzen die Image Eigenschaft):

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

Und der Stil in 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> 

Jetzt habe ich ein paar Fragen:

  1. Welche dieser Methoden ist besser geeignet, um eine benutzerdefinierte Schaltfläche zu erstellen?
  2. Mit beiden Methoden habe ich Problem beim Zuordnen der Klick-Funktion, in der Tat habe ich die System.Windows.Markup.XamlParseException, weil WPF die Zeichenfolge btnLogin_Click in der Funktion Click nicht konvertieren kann.
  3. Ich habe nie Generic.xaml verwendet, ist mein Beispiel korrekt oder sollte in irgendeiner Weise verbessert werden?
+0

Suchen Sie nur nach einer Möglichkeit, für jede Instanz ein anderes Bild zu übergeben? –

+0

Eine andere Bild-, Text- und Klickfunktion;) – Rowandish

+0

Gibt es einen bestimmten Grund, warum Sie ein benutzerdefiniertes Steuerelement dann möchten? Weil Sie all das mit nur einer Schaltflächenstilvorlage tun können. –

Antwort

1

Alrighty, so dass die meisten Rahmenelemente haben eine Handy-Dandy Eigenschaft Tag genannt, die für Instanzen wie diese liegt vor, wenn wir einen Weg, etwas in eine Vorlage Huckepack-verwenden konnten, ohne zusätzliche Abhängigkeitseigenschaften zu gehen und zu erklären und eine solche.

Wenn wir also eine Standard Button Stilvorlage übernehmen (Rechtsklick auf den Knopf-> Bearbeiten Template-> Bearbeiten eine Kopie) sehen wir ein ContentPresenter da drin, dass allgemein jedes CLR Objekt wird vorübergehen. Wir sind also gut für deinen Text oder was immer du willst.

Wir haben jetzt mehrere Optionen, um Ihr Ziel zu erreichen. Einer wäre, deine zwei Elemente einfach (in Pseudo) auf diese Weise zu übergeben;

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

Ausgenommen das scheint ein bisschen langweilig.Also bewegen wir uns in die Style Template-Option und machen stattdessen so etwas dazu;

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

Hinweis das addierte Image Element mit seinem Source Set {TemplateBinding Tag} zusammen mit einem Setter an der Spitze einen String Pfad zu spezifizieren, was ein Standard-Bild sein könnte.

So jetzt in der Instanz konnten wir nur tun;

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

... und wir würden eine Schaltfläche mit dem Standardsymbol und Ihrem Klick-Handler erhalten. Außer jetzt müssen wir verschiedene Symbole für jede Instanz anzeigen. Also könnten wir so etwas tun;

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

Ausgenommen das scheint immer noch ein wenig entmutigend. Wir könnten also einen Schritt weiter gehen und Ihre Asset-Strings zu Ressourcen machen. Fügen Sie den mscorlib Namespace Sie als so etwas wie `Xmlns Wörterbuch: sys“ Präfix und man konnte dies tun,

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

Dies tut uns einige Vorteile One, viel einfacher Wartung im Fall müssen Sie den Dateipfad ändern./Name für eines dieser Symbole Sie können es an nur einer Stelle tun, und er wird für alle Instanzen erben.Nicht jede Instanz aufspüren zu müssen, auf die es eingestellt ist, erlaubt es uns auch, diese in der Instanz als eine Ressource aufzurufen , so dass nun unsere Button Beispiel würde so aussehen,.

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

Voila, du bist fertig jedoch eine andere Beobachtung man beachten sollte, ist Ihr Dateipfade. Ich würde definitiv Pack Uri stattdessen verwenden. Solange dein Pfad korrekt ist und deine Build-Aktion auf dem Bild auch ist, solltest du eingestellt sein.

Hoffe das hilft, Prost.

+0

Ich habe das Problem mit der Datenkonvertierung gefunden: (https://social.msdn.microsoft.com/Forums/vstudio/en-US/edec50f6-d1c7-4891-a814-a9c6f154baf9/binding-image-source-with-templatebinding- in-xaml? forum = wpf), Am Ende war das Update Source = "{Binding Path = Tag, RelativeSource = {RelativeSource TemplatedParent}}". Sie können die Antwort beheben und ich werde es akzeptieren;) – Rowandish

+0

@Rowandish Oh, richtig, WPF, für alles andere ist es nur '{TemplateBinding Tag}' aber ich bin froh, dass du eine Lösung hast. :) –

Verwandte Themen