views:

37

answers:

2

I'm using Silverlight, but I'd be interested in a WPF answer as well

I have a list that is databound to an linked list of “Favorites”. Each favorite contains a name and a phone number.

The list is bound to a DataTemplate that describes the graphical aspects. In the this template is a button – Dial. When you click on that button I want the Dial() method of the Favorite to be called. Right now the Dial method of the page/window is called.

If this is not possible is there a way I can get the Favorite to somehow be attached to the Button? such that I know which Favorite was associated with the button press?

the below XAML does not work, Text="{Binding Name}" works great as it binds to the Name property on the Favorite, but Click="{Binding Dial}" does not call Dial() on the Favorite.

    <DataTemplate x:Key="DataTemplate1">
        <StackPanel d:DesignWidth="633" Orientation="Horizontal" Height="93">
            <Button x:Name="DialButton" Content="Edit" Click="{Binding Dial}"/>
            <TextBlock x:Name="TextBlock" TextWrapping="Wrap" Text="{Binding Name}" FontSize="64" Height="Auto" FontFamily="Segoe WP SemiLight"/>                                      
        </StackPanel>
    </DataTemplate>
+3  A: 

So it should go:

<Button CommandParameter="{Binding}" Command="{Binding Dial}"/>

Then you will receive the data object as the command parameter. In this scenario you must provide a Property that is called Dial and returns an ICommand-implementation. If the property is not available on your data-object but on the main class (code-behind), you must look for it within the binding, use for this the RelativeSource keyword.


Another way is to make a click handler. In the click handler you can cast the sender to a Button (or FrameworkElement) and then get the data object from the DataContext. I assume you tried to create such a solution.

private void Button_Click(object sender, RoutedEventArgs e) {
    Button btn = (Button)sender;
    MyObject obj = btn.DataContext as MyObject; 
    if(null != obj){
         obj.Dial();
         // or Dial(obj);
    }
}

The markup must be as follows:

<Button x:Name="DialButton" Content="Edit" Click="Button_Click"/> 

The main difference is, that I removed the binding from the Click-Event and registered an event-handler.


A third solution would be, to register a handler in the code behind for the Button.ClickEvent. The principle is similiar as in the second example.

I don't know silverlight very well. Perhaps there are the things a little bit other.

HCL
+1  A: 

HappyClicker's first solution is the best one for most purposes, since it supports good design patterns such as MVVM.

There is another simple way to get the same result using an attached property, so you can write:

<Button Content="Edit" my:RouteToContext.Click="Edit" />

and the Edit() method will be called on the button's DataContext.

Here is how the RouteToContext class might be implemented:

  public class RouteToContext : DependencyObject
  {
    public static string GetClick(FrameworkElement element) { return (string)element.GetValue(ClickProperty); }
    public static void SetClick(FrameworkElement element, string value) { element.SetValue(ClickProperty, value); }
    public static DependencyProperty ClickProperty = ConstructEventProperty("Click");

    // Additional proprties can be defined here

    private static DependencyProperty ConstructEventProperty(string propertyName)
    {
      return DependencyProperty.RegisterAttached(
        propertyName, typeof(string), typeof(RouteToContext),
        new PropertyMetadata
        {
          PropertyChangedCallback = (obj, propertyChangeArgs) =>
            obj.GetType().GetEvent(propertyName)
              .AddEventHandler(obj, new RoutedEventHandler((sender, eventArgs) =>
                ((FrameworkElement)sender).DataContext
                  .GetType().GetMethod((string)propertyChangeArgs.NewValue)
                  .Invoke(((FrameworkElement)sender).DataContext,
                    new object[] { sender, eventArgs }
                  )
              ))
        }
      );
    }
  }

How it works: When the RouteToContext.Click attached property is set, Type.GetEvent() is used to find the event named "Click", and an event handler is added to it. This event handler uses Type.GetMethod() to find the specified method on the DataContext, then invokes the method on the DataContext, passing the same sender and eventArgs it received.

Ray Burns
+1 Wow, this one is cool!
HCL