<
Style x:Key="MyStyle" TargetType="{x:Type ContentControl}"><
Setter Property="Template"><
Setter.Value><
ControlTemplate><
StackPanel Orientation="Horizontal" Margin="1,0,1,0" ><
TextBlock VerticalAlignment="Center" Margin="3"><
ContentPresenter Content="{TemplateBinding Property=ContentControl.Content}"/></
TextBlock><
Button Width="16" Height="16" Background="LightBlue" VerticalAlignment="Center" Margin="3"></
Button></
StackPanel></
ControlTemplate></
Setter.Value></
Setter></
Style>I want to apply this style and its corresponding ControlTemplate in several places in my program, so I have defined the style in the Application.Resources block in MyApp.xaml. I also want to handle the Click event of the button in my C# code. Is there a way to set a Click event handler in XAML at the point where I declare a ContentControl that uses this Style The problem that I'm having is that the Click event applies to the button, not to a ContentControl. So adding something like Button.Click="MyClickHandler" to the ContentControl doesn't work. I can add a Click property to the XAML for the button in the ControlTemplate definition, but then every instance of my control needs to share a single click handler, and that handler needs to be in MyApp, which won't work. I looked at using a ContentPresenter, but those can only set properties, and Click is an event, not a property. An EventTrigger seems close to what I need, but that only works for starting animations, not for invoking user-written event handlers. I also looked at using a CommandBinding, but it seems like that would direct the Click event for all buttons in my window to a single handler. I suppose that could be made to work, but it wouldn't be pretty.
I then experimented with setting up the click handler in code rather than XAML. I overrode OnInitialized() for my window and traversed the VisualTree for my ContentControl. I was able to get the FrameworkElementFactory for the button, but when I called AddHandler() on that factory, I got an error "After a FrameworkElementFactory is in use (sealed), it cannot be modified." There's an AddHandler() method on the UIElement class, which might work, but I can't figure out how to find the UIElement for a child of a control.
Is there any way to make this work

How do I handle a click event for a button in a ControlTemplate?
LordActon
This doesn't seem very useful, at least not in this case. Since this is setting an event handler on the style, doesn't every control that uses that style have to share a single event handler I could have done that by just adding Click = "OnClick" to the Button definition in my ControlTemplate.
The EventSetter would work well if there is a way in XAML to use something like a ContentPresenter to set the Handler property. But I don't think that will work, since an event handler isn't the content of a ContentControl.
Tom
HK
Tom
Mime Cuvalo
I'm not quite sure how Microsoft does it under the covers. I still haven't had a chance to go play with WPF lately, but when I do I'm gonna dissect the ScrollBar control and see what they do there.
wmerydith
Thanks for your response. Unfortunately, I have not been able to get this to work. I added an x:Name property to the Button in my ControlTemplate. I also added some code to the OnInitialized() method for my page that calls FindName(). If I call FindName() on the Page using the name of the ContentControl, it finds the control. But I tried calling FindName() on the page, the content control, and they content control's style, passing in the name of the button, and I always get back null. It must be possible to get this to work, since Microsoft apparently uses this technique, but it's not obvious to me how to do it.
Tom
flakzeus
HTH,
Drew
Luciano4
You're basically talking about defining a contract for a template. This has been discussed before in the newsgroups and there's no real approach set in stone for accomplishing it. The best thing to do though is require that they name the button with a well known name that you can then reach into the instance and find the button using INameScope::FindName. Once you have the button you hook the event handler and you're off and running.
HTH,
Drew
InTheDark
Calling FindName on the control that's tempated certainly seems like the right way to do it, since that would provide the ability to use a separate event handler for each instance of the control. However, it doesn't work. MyControl.FindName("MyButton") returns null.
I'm now trying a different approach: using a custom command which I add to the command bindings on my templated control. That almost works, although I'm experiencing some flaky behavior. I'll describe the problem in detail in another post.
hoangle
Thank you very much for your response. It gave me two different ways to solve my problem.
This works perfectly (although the order of the arguments to FindName is reversed). So in my case I have:
Button aButton = (Button)MyContentControl.Template.FindName("MyButton", MyContentControl);
aButton.AddHandler(Button.ClickEvent, new RoutedEventHandler(myClickHandler));
I've been looking at the Sells/Griffiths WPS book, the MSDN documentation, and various sample code, and this is the first I've heard of the EventSetter class. The MSDN documentation for this class is minimal, and I haven't been able to figure out how to use it. Could you provide an example
You're right, though, that it's possible to attach a listener to an object higher in the tree. In my case I was able to do the following:
<ContentControl Style="{StaticResource MyStyle}" Button.Click="myClickHandler" />
I thought that I tried that before and it didn't work, but it's working now.
The only drawback to this is that if there is more than one button in a templated control, all the buttons must share the same handler. The handler would have to determine which button was clicked. Using the first method (above), it's possible for each button to have its own handler.
Thanks again for your help.
Tom
aladdinm
EventSetter eventSetter = new EventSetter();
eventSetter.Event = FrameworkElement.LoadedEvent;
eventSetter.Handler = new RoutedEventHandler(OnLoaded);
style.Setters.Add( eventSetter );
LonnieBest
templatedElement.Template.FindName(templatedElement, "name")
Not sure I understand that question that started this all, though. Button.Click is a bubbling routed event, so you should be able to attach a listener to any object using attached event syntax. In a style, that would be done using EventSetter. What was the code that didn't work Thanks.
Fatming
Hey, I only read part of your post before and when I saw you mention OnInitialize I stopped. I should have read the whole thing because you also mentioned you're calling FindName on the Page, but I believe you need to use FindName on the control that's templated becauase the template creates its own name scope.
I wish I had some time to go whip up a demo to check this for you, but I'm stuck in the middle of something right now. Let me know if you still can't get it to work and I'll take a look at it.
Cheers,
Drew