none
Command an Image binden? RRS feed

  • Frage

  • Hallo!

    Ich möchte an ein Image ein Command binden, über das aktuelle Werte aus der Datenbank gelesen werden.
    Grob gesagt stelle ich mir das so vor:

    <Image Command="{Binding Path=RefreshCommand}" Source="Refresh.png" />

    Nur dummerweise hat Image keine Command Property.
    Mit einem Button wäre das Ganze problemlos möglich:

    <Button Command="{Binding Path=RefreshCommand}">
      <Image Source="Refresh.png" />
    </Button>
    

    Bei dieser Lösung besteht allerdings das Problem, dass dieser typische Button-Rand um das Image bestehen bleibt. Und eben nicht wirklich das Image das Command ausführt, sondern der Button.

    Gibts also irgendwie die Möglichkeit (ohne die Button-Lösung) ein Command direkt an ein Image zu binden?

     

    Schöne Gräße
    Martin

    Donnerstag, 10. Juni 2010 18:37

Antworten

  • Hi Martin,
    Könntest du mir noch die Alternative mit dem ControlTemplate erklären?

    Klaus' Vorschlag z.B. würde gehen, oder Du definierst einen wiederverwendbaren Style (in den Du noch andere Dinge dazupacken könntest) ...

    <Style x:Key="ImageCommandButtonStyle" TargetType="{x:Type Button}">
     <Setter Property="Control.Template">
     <Setter.Value>
      <ControlTemplate TargetType="{x:Type Button}">
      <ContentPresenter Content="{TemplateBinding Content}"/>
      </ControlTemplate>
     </Setter.Value>
     </Setter>
    </Style>
    

    ... und verwendest diesen dann entsprechend:

    <Button Style="{StaticResource ImageCommandButtonStyle}" Command="{Binding UpCommand}">
     <Image Source="/Resources/On.png" Width="16" Height="16"/>
    </Button>
    
    Davon ableitend kann man natürlich auch gleich ein UserControl basteln, das es wesentlich einfacher macht, Deinen "ImageButton" mit minimalem Aufwand wiederzuverwenden. Hier mal ein vollständiges Beispiel mit UserControl (UC ) und einem Window. Zunächst das UC:
    <UserControl x:Class="WpfTests.ImageCommandButton_UserControl"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="clr-namespace:WpfTests"
      Height="Auto" Width="Auto">
     <UserControl.Resources>
     <Style TargetType="{x:Type Button}">
      <Setter Property="Control.Template">
      <Setter.Value>
      <ControlTemplate TargetType="{x:Type Button}">
       <Grid>
       <ContentPresenter Content="{TemplateBinding Content}"/>
       <Rectangle x:Name="rctOverlay" Fill="White"/>
       </Grid>
       <ControlTemplate.Triggers>
       <Trigger Property="IsEnabled" Value="False">
       <Setter TargetName="rctOverlay" Property="Opacity" Value="0.5" />
       </Trigger>
       <Trigger Property="IsEnabled" Value="True">
       <Setter TargetName="rctOverlay" Property="Opacity" Value="0" />
       </Trigger>
       </ControlTemplate.Triggers>
      </ControlTemplate>
      </Setter.Value>
      </Setter>
     </Style>
     </UserControl.Resources>
     <Grid>
     <Grid.RowDefinitions>
      <RowDefinition Height="Auto"/>
     </Grid.RowDefinitions>
     <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto"/>
     </Grid.ColumnDefinitions>
     <Button Command="{Binding RelativeSource={
      RelativeSource FindAncestor, AncestorType={x:Type local:ImageCommandButton_UserControl}
      }, Path=Command}">
      <Image Source="{Binding RelativeSource={
      RelativeSource FindAncestor, AncestorType={x:Type local:ImageCommandButton_UserControl}
      }, Path=ImageSource}"/>
     </Button>
     </Grid>
    </UserControl>
    

    Code-behind des UCs:

    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Windows.Media;
    
    namespace WpfTests
    {
     public partial class ImageCommandButton_UserControl : UserControl
     {
     public ImageCommandButton_UserControl()
     {
      InitializeComponent();
     }
    
     public static readonly DependencyProperty CommandProperty =
      DependencyProperty.Register(
      "Command",
      typeof(ICommand),
      typeof(ImageCommandButton_UserControl),
      new PropertyMetadata(null)
      );
     public ICommand Command
     {
      get { return (ICommand)GetValue(CommandProperty); }
      set { SetValue(CommandProperty, value); }
     }
    
     public static readonly DependencyProperty ImageSourceProperty =
      DependencyProperty.Register(
      "ImageSource",
      typeof(ImageSource),
      typeof(ImageCommandButton_UserControl),
      new PropertyMetadata(null)
      );
     public ImageSource ImageSource
     {
      get { return (ImageSource)GetValue(ImageSourceProperty); }
      set { SetValue(ImageSourceProperty, value); }
     }
     }
    }
    

    Sprich, innerhalb des UCs definierst Du zwei DependencyProperties, die das Button-Command und die ImageSource nach außen reichen. Im XAML ist das etwas verschlüsselte Binding wichtig, weil darüber sichergestellt wird, dass der Button innerhalb des UCs sein eigenes ICommand verwendet. Das Grid, in das ich ihn gepackt habe, stellt sicher, dass sich die Größe des UCs am gebundenen Image orientiert. Im Style habe ich als Beispiel mal ein Rectangle dazugepackt, über das das CanExecute des ICommands auf einfache Art visualisiert wird, indem einfach ein transluzentes Rechteck darübergepackt wird - man könnte natürlich auch versucht sein, gleich einen GrayScale-PixelShader dahinterzulegen, aber das überlasse ich dann Dir. :-)

    Die Verwendung des UCs innerhalb eines Windows ist dann trivial:

    <Window x:Class="WpfTests.ImageCommandButton_Window"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:local="clr-namespace:WpfTests"
     Title="ImageCommandButton_Window" Height="300" Width="300">
     <StackPanel>
     <CheckBox IsChecked="{Binding ButtonCommand.CmdIsEnabled}" 
      Content="Command enabled?"
      Margin="10"/>
    
     <local:ImageCommandButton_UserControl 
      Grid.Row="1"
      Command="{Binding ButtonCommand}" 
      Margin="10"
      ImageSource="/Resources/SampleImage.png"/>
     </StackPanel>
    </Window>
    

    Code-behind des Windows:

    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    
    namespace WpfTests
    {
     public partial class ImageCommandButton_Window : Window
     {
     public ICommand ButtonCommand { get; set; }
    
     public ImageCommandButton_Window()
     {
      InitializeComponent();
      ButtonCommand = new SampleButtonCommand();
      this.DataContext = this;
     }
     }
    
     public class SampleButtonCommand : ICommand
     {
     public bool CmdIsEnabled { get; set; }
    
     public Boolean CanExecute(Object parameter) { return CmdIsEnabled; }
    
     public event EventHandler CanExecuteChanged
     {
      add { CommandManager.RequerySuggested += value; }
      remove { CommandManager.RequerySuggested -= value; }
     }
    
     public void Execute(Object parameter)
     {
      MessageBox.Show("You clicked the Button.");
     }
     }
    }
    

    Die Checkbox im obigen Fenster ist nur darin, um den Disabled-Effekt auch zur Laufzeit darstellen zu können.


    Cheers,
    Olaf
    http://blogs.intuidev.com
    • Bearbeitet Olaf Rabbachin Samstag, 12. Juni 2010 09:34 Zu Weihnachten wünsche ich mir, dass sich endlich mal jemand um diesen !#%$§ CodeFormatter kümmert, der das auch hinbekommt!
    • Als Antwort markiert Martin_78 Sonntag, 13. Juni 2010 15:19
    Samstag, 12. Juni 2010 09:29
  • Hi Martin,
    Ich habe das UserControl ein bisschen umgebaut. Und zwar derart, dass die Opacity bei 0.25 liegt, wenn CanExecute false ist und im "Normalzustand" bei 0.5 liegt. Wenn man mit der Maus über das Control fährt, ist die Opacity 1.0

    [XAML ...]

    BTW - da machst Du's Dir zu schwer, das gleiche Ergebnis bekommst Du mit einer "normalen" DoubleAnimation auf einfachere Weise, denn da brauchst Du Dich erst gar nicht um den aktuellen Wert (Dein erstes Spline) kümmern und lediglich das Ziel (To ) setzen. Schau mal in das Beispiel in diesem Thread (aus dem en-Forum) .

     

    Was müsste ich jetzt nur noch anstellen, um bei diesem ImageCommandButton Events nutzen zu können?

    Wenn ich folgendes versuche, führt das nämlich zu keinem Ergebnis:

    [XAML ...]

    Soll heißen auf den Event MouseLeftButtonDown wird nicht reagiert und die entsprechende Methode in der Code Behind Datei wird nicht ausgeführt.

    Hm, jetzt bin ich eigentlich baff, denn ich war davon ausgegangen, dass Du das ICommand wegen MVVM verwenden wolltest, daher ja überhaupt der ganze Aufwand! :-)
    Soll heißen, wenn Du "nur" im Code auf den Click reagieren möchtest, könntest Du doch auch ausschließlich ein Image anstelle des UCs nehmen und einfach auf dessen MouseLeftButtonDown reagieren!?

    Aber wie auch immer, der einfache Weg ginge, wie von Klaus beschrieben, über die Preview*-Events. Diese berücksichtigen aber nicht, ob das Control selbst aktiv (.IsEnabled=True ) ist, vielmehr wird der Event immer ausgelöst. Da ich das UC ja eigentlich auf ICommand abgestellt wurde, gibt's auch keine IsEnabled Implementierung darin, denn das läuft ja über das ICommand, an das gebunden wird. Du könntest natürlich auch noch eine IsEnabled DP dazupacken.

    Davon mal ganz abgesehen, kannst Du aber natürlich auch beides (ICommand, Click-Event) im UC ermöglichen. Für den (zus.) Click-Event müsstest Du im UC folgendes noch einbauen:

    //Event-Routing
    public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent(
      "Click", 
      RoutingStrategy.Bubble,
      typeof(RoutedEventHandler), 
      typeof(ImageCommandButton_UserControl)
     );
    
    //CLR-Accessor
    public event RoutedEventHandler Click
    {
     add { AddHandler(ClickEvent, value); }
     remove { RemoveHandler(ClickEvent, value); }
    }
    
    //Raiser
    private void RaiseClickEvent()
    {
     RoutedEventArgs newEventArgs = new RoutedEventArgs(ClickEvent, this);
     RaiseEvent(newEventArgs);
    }
    
    //Local handler, raising/publishing the routed event.
    private void cmd_Click(object sender, RoutedEventArgs e)
    {
     RaiseClickEvent();
    }
    

     

    Der Button im UC müsste nun noch einen Namen verpasst bekommen und den Click-Event feuern:

     

    <Button x:Name="cmd" 
        Command="{Binding RelativeSource={
         RelativeSource FindAncestor, AncestorType={x:Type local:ImageCommandButton_UserControl}
        }, Path=Command}"
        Click="cmd_Click">
     <Image Source="{Binding RelativeSource={
        RelativeSource FindAncestor, AncestorType={x:Type local:ImageCommandButton_UserControl}
       }, Path=ImageSource}"/>
    </Button>
    
    Im das UC verwendenden Window steht Dir dann wieder ein Click-Event zur Verfügung, an den Du dann Deinen Code kleben kannst.

     


    Cheers,
    Olaf
    http://blogs.intuidev.com
    • Bearbeitet Olaf Rabbachin Sonntag, 13. Juni 2010 08:48 Aaaaaaaaaah - schon wieder der Code-Formatierer! Sorry - XAML im Quote mag er anscheinend auch nicht, musste ich löschen ...
    • Als Antwort markiert Martin_78 Sonntag, 13. Juni 2010 15:20
    Sonntag, 13. Juni 2010 08:46

Alle Antworten

  • Hi Martin,

    Du könntest ein AttachedCommandBehavior verwenden. Marlon Grech hat dazu was in seinem Blog . Alternativ könntest Du natürlich auch einen ControlTemplate für Buttons erzeugen, das das Control quasi unsichtbar macht und dann diesen verwenden.


    Cheers,
    Olaf
    http://blogs.intuidev.com
    Freitag, 11. Juni 2010 06:40
  • Hallo Martin_78,

    Möchtest Du nicht das Click Ereignis verwenden?

    [XAML]

     

    <Window x:Class="WpfApplication1.Window1"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      Title="Window1" Height="300" Width="300">
      <Grid>
        <Image Margin="33,44,57,68" Name="image1" Stretch="Uniform" Source="C:\Temp\Employee.jpg" MouseLeftButtonDown="image1_MouseLeftButtonDown" />
      </Grid>
    </Window>

     

    [C# Code behind]

    using System;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    
    
    
    namespace WpfApplication1
    {
      /// <summary>
      /// Interaction logic for Window1.xaml
      /// </summary>
      public partial class Window1 : Window
      {
        public Window1()
        {
          InitializeComponent();
        }
    
        private void image1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
          image1 = (Image)sender;
    
          if (e.ClickCount == 1) //Einfaches click (Linker Mouse Knopf)
          {
            MessageBox.Show("Einfaches click (Linker Mouse Knopf)");
    
            // code fogt
          }
    
        }
      }
    }

     

    Grüße,

    Robert

    Freitag, 11. Juni 2010 07:39
    Moderator
  • Hallo Martin_78,

    Möchtest Du nicht das Click Ereignis verwenden?

    [...]

    Hallo Robert!

    Nein, das hilft mir leider nicht viel weiter, da ich ja den Klick auf das Image ja an ein Command innerhalb des Viewmodels binden möchte, dass an den DataContext des UserControls gebunden ist.

    Oder gibt es eine Möglichkeit aus dem Code Behind per Code an das Command zu binden bzw. es auszuführen?

     

     

    Hi Martin,

    Du könntest ein AttachedCommandBehavior verwenden. Marlon Grech hat dazu was in seinem Blog . Alternativ könntest Du natürlich auch einen ControlTemplate für Buttons erzeugen, das das Control quasi unsichtbar macht und dann diesen verwenden.


    Cheers,
    Olaf
    http://blogs.intuidev.com

     

    Hallo Olaf!

    Ja vielen Dank, mit dem AttachedCommandBehaviour klappt das Ganze 1A!
    Es ist nur leider verhältnismäßig viel Overhead, finde ich.

    Könntest du mir noch die Alternative mit dem ControlTemplate erklären?

    Gruß
    Martin

    Freitag, 11. Juni 2010 18:10
  • Probier's doch mal so:
    <Button Command="CorrectionList">
      <Button.Template>
        <ControlTemplate>
          <Image Source="/WpfApplication1;component/Images/brain.png" />
        </ControlTemplate>
      </Button.Template>
    </Button>

    Dann wird, wenn du auf das Bild klickst, das Command CorrectionList aufgerufen, das du dann binden kannst.

    Gruß

       Klaus

    Freitag, 11. Juni 2010 23:46
  • Hi Martin,
    hier mal eine Demo mit Klaus' Beitrag für MVVM:
     
    XAML:
     
    <Window x:Class="MainWindow"
        xmlns="
    http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="
    http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
            xmlns:my="clr-namespace:WpfApplication1">
      <Window.Resources>
        <my:ViewModel x:Key="vm"/>
      </Window.Resources>
      <Grid DataContext="{Binding Source={StaticResource vm}}">
        <Button Command="{Binding cmdRefresh}" Width="200" Height="200">
          <Button.Template>
            <ControlTemplate>
              <Image Source="{Binding Bild}" Stretch="Fill" />
            </ControlTemplate>
          </Button.Template>
        </Button>
      </Grid>
    </Window>
     
    Und dazu der ViewModel:
     
    Imports System.ComponentModel
    Imports System.Collections.ObjectModel
     
    Public Class ViewModel
      Implements INotifyPropertyChanged
      Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
     
      Private bildliste() As String = {"Bild1.jpg", "Bild2.jpg"}
      Private index As Integer = 0
     
      Public Sub New()
        BildLaden(Nothing)
      End Sub
     
      Public Property Bild As BitmapImage
     
      Public ReadOnly Property cmdRefresh As ICommand
        Get
          Return New RelayCommand(New Action(Of Object)(AddressOf BildLaden))
        End Get
      End Property
     
      Private Sub BildLaden(ByVal state As Object)
        SyncLock Me
          Dim bmi As New BitmapImage
          With bmi
            ..BeginInit()
            .UriSource = New Uri(bildliste(index), UriKind.Relative)
            ..EndInit()
          End With
          Bild = bmi
          RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("Bild"))
          index += 1
          If index > bildliste.GetUpperBound(0) Then index = 0
        End SyncLock
      End Sub
     
    End Class
     

    --
    Viele Gruesse
     
    Peter
     
     
     
     
    "Martin_78" schrieb im Newsbeitrag news:118ebba1-f398-4c72-8b73-26753d9bd752...

    Hallo Martin_78,

    Möchtest Du nicht das Click Ereignis verwenden?

    [...]

    Hallo Robert!

    Nein, das hilft mir leider nicht viel weiter, da ich ja den Klick auf das Image ja an ein Command innerhalb des Viewmodels binden möchte, dass an den DataContext des UserControls gebunden ist.

    Oder gibt es eine Möglichkeit aus dem Code Behind per Code an das Command zu binden bzw. es auszuführen?

     

     

    Hi Martin,

    Du könntest ein AttachedCommandBehavior verwenden. Marlon Grech hat dazu was in seinem Blog . Alternativ könntest Du natürlich auch einen ControlTemplate für Buttons erzeugen, das das Control quasi unsichtbar macht und dann diesen verwenden.


    Cheers,
    Olaf
    http://blogs.intuidev.com

     

    Hallo Olaf!

    Ja vielen Dank, mit dem AttachedCommandBehaviour klappt das Ganze 1A!
    Es ist nur leider verhältnismäßig viel Overhead, finde ich.

    Könntest du mir noch die Alternative mit dem ControlTemplate erklären?

    Gruß
    Martin

    Samstag, 12. Juni 2010 07:24
  • Hi Martin,
    Könntest du mir noch die Alternative mit dem ControlTemplate erklären?

    Klaus' Vorschlag z.B. würde gehen, oder Du definierst einen wiederverwendbaren Style (in den Du noch andere Dinge dazupacken könntest) ...

    <Style x:Key="ImageCommandButtonStyle" TargetType="{x:Type Button}">
     <Setter Property="Control.Template">
     <Setter.Value>
      <ControlTemplate TargetType="{x:Type Button}">
      <ContentPresenter Content="{TemplateBinding Content}"/>
      </ControlTemplate>
     </Setter.Value>
     </Setter>
    </Style>
    

    ... und verwendest diesen dann entsprechend:

    <Button Style="{StaticResource ImageCommandButtonStyle}" Command="{Binding UpCommand}">
     <Image Source="/Resources/On.png" Width="16" Height="16"/>
    </Button>
    
    Davon ableitend kann man natürlich auch gleich ein UserControl basteln, das es wesentlich einfacher macht, Deinen "ImageButton" mit minimalem Aufwand wiederzuverwenden. Hier mal ein vollständiges Beispiel mit UserControl (UC ) und einem Window. Zunächst das UC:
    <UserControl x:Class="WpfTests.ImageCommandButton_UserControl"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="clr-namespace:WpfTests"
      Height="Auto" Width="Auto">
     <UserControl.Resources>
     <Style TargetType="{x:Type Button}">
      <Setter Property="Control.Template">
      <Setter.Value>
      <ControlTemplate TargetType="{x:Type Button}">
       <Grid>
       <ContentPresenter Content="{TemplateBinding Content}"/>
       <Rectangle x:Name="rctOverlay" Fill="White"/>
       </Grid>
       <ControlTemplate.Triggers>
       <Trigger Property="IsEnabled" Value="False">
       <Setter TargetName="rctOverlay" Property="Opacity" Value="0.5" />
       </Trigger>
       <Trigger Property="IsEnabled" Value="True">
       <Setter TargetName="rctOverlay" Property="Opacity" Value="0" />
       </Trigger>
       </ControlTemplate.Triggers>
      </ControlTemplate>
      </Setter.Value>
      </Setter>
     </Style>
     </UserControl.Resources>
     <Grid>
     <Grid.RowDefinitions>
      <RowDefinition Height="Auto"/>
     </Grid.RowDefinitions>
     <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto"/>
     </Grid.ColumnDefinitions>
     <Button Command="{Binding RelativeSource={
      RelativeSource FindAncestor, AncestorType={x:Type local:ImageCommandButton_UserControl}
      }, Path=Command}">
      <Image Source="{Binding RelativeSource={
      RelativeSource FindAncestor, AncestorType={x:Type local:ImageCommandButton_UserControl}
      }, Path=ImageSource}"/>
     </Button>
     </Grid>
    </UserControl>
    

    Code-behind des UCs:

    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Windows.Media;
    
    namespace WpfTests
    {
     public partial class ImageCommandButton_UserControl : UserControl
     {
     public ImageCommandButton_UserControl()
     {
      InitializeComponent();
     }
    
     public static readonly DependencyProperty CommandProperty =
      DependencyProperty.Register(
      "Command",
      typeof(ICommand),
      typeof(ImageCommandButton_UserControl),
      new PropertyMetadata(null)
      );
     public ICommand Command
     {
      get { return (ICommand)GetValue(CommandProperty); }
      set { SetValue(CommandProperty, value); }
     }
    
     public static readonly DependencyProperty ImageSourceProperty =
      DependencyProperty.Register(
      "ImageSource",
      typeof(ImageSource),
      typeof(ImageCommandButton_UserControl),
      new PropertyMetadata(null)
      );
     public ImageSource ImageSource
     {
      get { return (ImageSource)GetValue(ImageSourceProperty); }
      set { SetValue(ImageSourceProperty, value); }
     }
     }
    }
    

    Sprich, innerhalb des UCs definierst Du zwei DependencyProperties, die das Button-Command und die ImageSource nach außen reichen. Im XAML ist das etwas verschlüsselte Binding wichtig, weil darüber sichergestellt wird, dass der Button innerhalb des UCs sein eigenes ICommand verwendet. Das Grid, in das ich ihn gepackt habe, stellt sicher, dass sich die Größe des UCs am gebundenen Image orientiert. Im Style habe ich als Beispiel mal ein Rectangle dazugepackt, über das das CanExecute des ICommands auf einfache Art visualisiert wird, indem einfach ein transluzentes Rechteck darübergepackt wird - man könnte natürlich auch versucht sein, gleich einen GrayScale-PixelShader dahinterzulegen, aber das überlasse ich dann Dir. :-)

    Die Verwendung des UCs innerhalb eines Windows ist dann trivial:

    <Window x:Class="WpfTests.ImageCommandButton_Window"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:local="clr-namespace:WpfTests"
     Title="ImageCommandButton_Window" Height="300" Width="300">
     <StackPanel>
     <CheckBox IsChecked="{Binding ButtonCommand.CmdIsEnabled}" 
      Content="Command enabled?"
      Margin="10"/>
    
     <local:ImageCommandButton_UserControl 
      Grid.Row="1"
      Command="{Binding ButtonCommand}" 
      Margin="10"
      ImageSource="/Resources/SampleImage.png"/>
     </StackPanel>
    </Window>
    

    Code-behind des Windows:

    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    
    namespace WpfTests
    {
     public partial class ImageCommandButton_Window : Window
     {
     public ICommand ButtonCommand { get; set; }
    
     public ImageCommandButton_Window()
     {
      InitializeComponent();
      ButtonCommand = new SampleButtonCommand();
      this.DataContext = this;
     }
     }
    
     public class SampleButtonCommand : ICommand
     {
     public bool CmdIsEnabled { get; set; }
    
     public Boolean CanExecute(Object parameter) { return CmdIsEnabled; }
    
     public event EventHandler CanExecuteChanged
     {
      add { CommandManager.RequerySuggested += value; }
      remove { CommandManager.RequerySuggested -= value; }
     }
    
     public void Execute(Object parameter)
     {
      MessageBox.Show("You clicked the Button.");
     }
     }
    }
    

    Die Checkbox im obigen Fenster ist nur darin, um den Disabled-Effekt auch zur Laufzeit darstellen zu können.


    Cheers,
    Olaf
    http://blogs.intuidev.com
    • Bearbeitet Olaf Rabbachin Samstag, 12. Juni 2010 09:34 Zu Weihnachten wünsche ich mir, dass sich endlich mal jemand um diesen !#%$§ CodeFormatter kümmert, der das auch hinbekommt!
    • Als Antwort markiert Martin_78 Sonntag, 13. Juni 2010 15:19
    Samstag, 12. Juni 2010 09:29
  • Hallo Olaf!

    Ja super, sowohl die Kurzform als auch die UserControl-Variante klappen wunder bar.

    Ich habe das UserControl ein bisschen umgebaut. Und zwar derart, dass die Opacity bei 0.25 liegt, wenn CanExecute false ist und im "Normalzustand" bei 0.5 liegt. Wenn man mit der Maus über das Control fährt, ist die Opacity 1.0

    Das Ganze sieht so aus:

    <UserControl x:Class="WpfApplication5.ImageCommandButton"
           xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
           xmlns:local="clr-namespace:WpfApplication5">
      <UserControl.Resources>
        <Image x:Name="imgTrue" x:Key="imgTrue" Opacity="0.5"
            Source="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:ImageCommandButton}}, Path=ImageSource}">
          <Image.Triggers>
            <EventTrigger RoutedEvent="Mouse.MouseEnter">
              <BeginStoryboard>
                <Storyboard>
                  <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetProperty="(UIElement.Opacity)">
                    <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0.5"/>
                    <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="1"/>
                  </DoubleAnimationUsingKeyFrames>
                </Storyboard>
              </BeginStoryboard>
            </EventTrigger>
            <EventTrigger RoutedEvent="Mouse.MouseLeave">
              <BeginStoryboard>
                <Storyboard>
                  <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetProperty="(UIElement.Opacity)">
                    <SplineDoubleKeyFrame KeyTime="00:00:00" Value="1"/>
                    <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0.5"/>
                  </DoubleAnimationUsingKeyFrames>
                </Storyboard>
              </BeginStoryboard>
            </EventTrigger>
          </Image.Triggers>
        </Image>
        <Image x:Name="imgFalse" x:Key="imgFalse" Opacity="0.25" 
            Source="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:ImageCommandButton}}, Path=ImageSource}" />
        
        <Style TargetType="{x:Type Button}">
          <Setter Property="Control.Template">
            <Setter.Value>
              <ControlTemplate TargetType="{x:Type Button}">
                <Grid>
                  <ContentPresenter Content="{TemplateBinding Content}"/>
                </Grid>
                <ControlTemplate.Triggers>
                  <Trigger Property="IsEnabled" Value="False">
                    <Setter Property="Content" Value="{StaticResource imgFalse}" />
                  </Trigger>
                  <Trigger Property="IsEnabled" Value="True">
                    <Setter Property="Content" Value="{StaticResource imgTrue}" />
                  </Trigger>
                </ControlTemplate.Triggers>
              </ControlTemplate>
            </Setter.Value>
          </Setter>
        </Style>
      </UserControl.Resources>
    
      <Button Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:ImageCommandButton}}, Path=Command}" />
    </UserControl>

    Einzubinden dann auf die selbe Art:

    <local:ImageCommandButton Width="16" Height="16" 
      Command="{Binding RefreshCommand}" 
      ImageSource="/Images/Refresh.png"/>

    Klappt so, wie gesagt, wunderbarst ;)

    Was müsste ich jetzt nur noch anstellen, um bei diesem ImageCommandButton Events nutzen zu können?
    Wenn ich folgendes versuche, führt das nämlich zu keinem Ergebnis:

    <local:ImageCommandButton Width="16" Height="16" 
      MouseLeftButtonDown="imgClose_MouseLeftButtonDown"
      ImageSource="/Images/Close.png"/>

    Soll heißen auf den Event MouseLeftButtonDown wird nicht reagiert und die entsprechende Methode in der Code Behind Datei wird nicht ausgeführt.

    Irgendwelche Ideen? :)

    Samstag, 12. Juni 2010 15:43
  • Probier es doch mal mit dem entsprechenden Preview Event PreviewMouseDown. Das funktioniert.

    Ich denke es liegt daran, dass das Command Binding des Buttons dieses Event behandelt und das Handled Flag der RoutedEventArgs auf true gesetzt wird. Somit bekommst du dieses Event nicht mehr über das Event Bubbleing.

    http://msdn.microsoft.com/en-us/library/ms742806.aspx#how_event_processing_works

     

    Samstag, 12. Juni 2010 17:03
  • Hi Martin,
    Ich habe das UserControl ein bisschen umgebaut. Und zwar derart, dass die Opacity bei 0.25 liegt, wenn CanExecute false ist und im "Normalzustand" bei 0.5 liegt. Wenn man mit der Maus über das Control fährt, ist die Opacity 1.0

    [XAML ...]

    BTW - da machst Du's Dir zu schwer, das gleiche Ergebnis bekommst Du mit einer "normalen" DoubleAnimation auf einfachere Weise, denn da brauchst Du Dich erst gar nicht um den aktuellen Wert (Dein erstes Spline) kümmern und lediglich das Ziel (To ) setzen. Schau mal in das Beispiel in diesem Thread (aus dem en-Forum) .

     

    Was müsste ich jetzt nur noch anstellen, um bei diesem ImageCommandButton Events nutzen zu können?

    Wenn ich folgendes versuche, führt das nämlich zu keinem Ergebnis:

    [XAML ...]

    Soll heißen auf den Event MouseLeftButtonDown wird nicht reagiert und die entsprechende Methode in der Code Behind Datei wird nicht ausgeführt.

    Hm, jetzt bin ich eigentlich baff, denn ich war davon ausgegangen, dass Du das ICommand wegen MVVM verwenden wolltest, daher ja überhaupt der ganze Aufwand! :-)
    Soll heißen, wenn Du "nur" im Code auf den Click reagieren möchtest, könntest Du doch auch ausschließlich ein Image anstelle des UCs nehmen und einfach auf dessen MouseLeftButtonDown reagieren!?

    Aber wie auch immer, der einfache Weg ginge, wie von Klaus beschrieben, über die Preview*-Events. Diese berücksichtigen aber nicht, ob das Control selbst aktiv (.IsEnabled=True ) ist, vielmehr wird der Event immer ausgelöst. Da ich das UC ja eigentlich auf ICommand abgestellt wurde, gibt's auch keine IsEnabled Implementierung darin, denn das läuft ja über das ICommand, an das gebunden wird. Du könntest natürlich auch noch eine IsEnabled DP dazupacken.

    Davon mal ganz abgesehen, kannst Du aber natürlich auch beides (ICommand, Click-Event) im UC ermöglichen. Für den (zus.) Click-Event müsstest Du im UC folgendes noch einbauen:

    //Event-Routing
    public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent(
      "Click", 
      RoutingStrategy.Bubble,
      typeof(RoutedEventHandler), 
      typeof(ImageCommandButton_UserControl)
     );
    
    //CLR-Accessor
    public event RoutedEventHandler Click
    {
     add { AddHandler(ClickEvent, value); }
     remove { RemoveHandler(ClickEvent, value); }
    }
    
    //Raiser
    private void RaiseClickEvent()
    {
     RoutedEventArgs newEventArgs = new RoutedEventArgs(ClickEvent, this);
     RaiseEvent(newEventArgs);
    }
    
    //Local handler, raising/publishing the routed event.
    private void cmd_Click(object sender, RoutedEventArgs e)
    {
     RaiseClickEvent();
    }
    

     

    Der Button im UC müsste nun noch einen Namen verpasst bekommen und den Click-Event feuern:

     

    <Button x:Name="cmd" 
        Command="{Binding RelativeSource={
         RelativeSource FindAncestor, AncestorType={x:Type local:ImageCommandButton_UserControl}
        }, Path=Command}"
        Click="cmd_Click">
     <Image Source="{Binding RelativeSource={
        RelativeSource FindAncestor, AncestorType={x:Type local:ImageCommandButton_UserControl}
       }, Path=ImageSource}"/>
    </Button>
    
    Im das UC verwendenden Window steht Dir dann wieder ein Click-Event zur Verfügung, an den Du dann Deinen Code kleben kannst.

     


    Cheers,
    Olaf
    http://blogs.intuidev.com
    • Bearbeitet Olaf Rabbachin Sonntag, 13. Juni 2010 08:48 Aaaaaaaaaah - schon wieder der Code-Formatierer! Sorry - XAML im Quote mag er anscheinend auch nicht, musste ich löschen ...
    • Als Antwort markiert Martin_78 Sonntag, 13. Juni 2010 15:20
    Sonntag, 13. Juni 2010 08:46
  • Super, danke Klaus und Olaf! Passt :)

    Der Grund dafür, dass ich jetzt nun doch den Click Event brauchte, lag darin, dass zwar die meisten dieser ImageButtons an Commands gebunden werden sollen, einer jeder die Anwendung schließen soll, und ich dafür eben im Code Behind Close() aufrufen wollte.

    Ich hätte natürlich auch ein normales Image nehmen können, aber da hätte ich dann auch nochmal die ganzen Angaben zum rein- und rausfaden machen müssen. So ist das jetzt ne schöne einheitliche Sache :)

    Sonntag, 13. Juni 2010 15:19