locked
Code-behind for embedded button (chutch15) - 12/6/2007 6:36 AM PST RRS feed

  • Question

  • By: chutch15


    I have embedded a button inside the header bar of an expander within the 
    expander's template.  However, when I attempt to add an event handler (Click) 
    to the button, I am told that the document item does not have a code-behind 
    file and that I need to add a code-behind file and a class definition before 
    I can add an event handler.
    
    Not sure how to proceed.  Help?!
    -- 
    - Chad
    Thursday, February 21, 2008 9:04 PM

Answers

  • By: Corrado Cavalli [MVP]


    I suggest you to use bubbling event handling it at higher level:

    <Grid Button.Click="button1_Click">
    <StackPanel Name="stackPanel1" Height="80" VerticalAlignment="Top" />
    <Expander Header="Expander" Height="24.442" Margin="26.664,0,47.773,99.99"
    Name="expander1" VerticalAlignment="Bottom">
    <Expander.HeaderTemplate>
    <DataTemplate>
    <StackPanel Orientation="Horizontal">
    <Button>X</Button>
    <TextBlock Text="{Binding RelativeSource={RelativeSource
    TemplatedParent},Path=Content}" />
    </StackPanel>
    </DataTemplate>
    </Expander.HeaderTemplate>
    </Expander>
    </Grid>

    If you really need to handle event raised by the button inside the template
    you have to create the codebehind class for the element containing the
    template, e.g. if your template is defined inside a resourcedictionary
    you've to manually add a x:Class="YourNameSpace.ClassName" and define the
    codebehind partial class.

    public partial class YourNameSpace.ClassName:ResourceDictionary
    {
    public ClassName
    {
    InitializeComponent();
    }
    }

    HTH
    --
    Corrado Cavalli [Microsoft .NET MVP-MCP]
    UGIdotNET -
    http://www.ugidotnet.org
    Friday, February 22, 2008 8:01 PM

All replies

  • By: Corrado Cavalli [MVP]


    I suggest you to use bubbling event handling it at higher level:

    <Grid Button.Click="button1_Click">
    <StackPanel Name="stackPanel1" Height="80" VerticalAlignment="Top" />
    <Expander Header="Expander" Height="24.442" Margin="26.664,0,47.773,99.99"
    Name="expander1" VerticalAlignment="Bottom">
    <Expander.HeaderTemplate>
    <DataTemplate>
    <StackPanel Orientation="Horizontal">
    <Button>X</Button>
    <TextBlock Text="{Binding RelativeSource={RelativeSource
    TemplatedParent},Path=Content}" />
    </StackPanel>
    </DataTemplate>
    </Expander.HeaderTemplate>
    </Expander>
    </Grid>

    If you really need to handle event raised by the button inside the template
    you have to create the codebehind class for the element containing the
    template, e.g. if your template is defined inside a resourcedictionary
    you've to manually add a x:Class="YourNameSpace.ClassName" and define the
    codebehind partial class.

    public partial class YourNameSpace.ClassName:ResourceDictionary
    {
    public ClassName
    {
    InitializeComponent();
    }
    }

    HTH
    --
    Corrado Cavalli [Microsoft .NET MVP-MCP]
    UGIdotNET -
    http://www.ugidotnet.org
    Friday, February 22, 2008 8:01 PM
  • By: chutch15


    I'm not sure I understand all the subclass stuff (yet), but I get what you 
    mean by the bubbling.  I put the buttons inside the expander template because 
    I will have series of these expanders in a vertical stack panel and I wanted 
    to keep them uniform.  But then comes the problem of programming each button 
    to the different expanders to launch different popups.
    
    Any suggestions are welcome.  Thanks!
    -- 
    - Chad
    
    
    "Corrado Cavalli [MVP]" wrote:
    
    > I suggest you to use bubbling event handling it at higher level:
    > 
    > <Grid Button.Click="button1_Click">
    >   <StackPanel  Name="stackPanel1" Height="80" VerticalAlignment="Top" />
    >   <Expander Header="Expander" Height="24.442" Margin="26.664,0,47.773,99.99" 
    > Name="expander1" VerticalAlignment="Bottom">
    >    <Expander.HeaderTemplate>
    >     <DataTemplate>
    >      <StackPanel Orientation="Horizontal">
    >       <Button>X</Button>
    >       <TextBlock Text="{Binding RelativeSource={RelativeSource 
    > TemplatedParent},Path=Content}" />
    >      </StackPanel>
    >     </DataTemplate>
    >    </Expander.HeaderTemplate>
    >   </Expander>
    >  </Grid>
    > 
    > If you really need to handle event raised by the button inside the template 
    > you have to create the codebehind class for the element containing the 
    > template, e.g. if your template is defined inside a resourcedictionary 
    > you've to manually add a x:Class="YourNameSpace.ClassName" and define the 
    > codebehind partial class.
    > 
    > public partial class YourNameSpace.ClassName:ResourceDictionary
    > {
    >   public ClassName
    >  {
    >    InitializeComponent();
    >  }
    > }
    > 
    > HTH
    > -- 
    > Corrado Cavalli [Microsoft .NET MVP-MCP]
    > UGIdotNET - http://www.ugidotnet.org 
    > 
    > 
    Friday, February 22, 2008 8:02 PM
  • By: Corrado Cavalli [MVP]


    I think that for your need nothing a command would be the best solution,
    here's a snippet:

    <Window x:Class="TestWPF.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">
    <Window.CommandBindings>
    <CommandBinding Command="ApplicationCommands.Open"
    Executed="CommandBinding_Executed" />
    </Window.CommandBindings>
    <Grid>
    <StackPanel Name="stackPanel1" Height="80" VerticalAlignment="Top" />
    <Expander Tag="param1" Header="Expander" Height="24.442"
    Margin="26.664,0,47.773,99.99" Name="expander1" VerticalAlignment="Bottom">
    <Expander.HeaderTemplate>
    <DataTemplate>
    <StackPanel Orientation="Horizontal">
    <Button Command="ApplicationCommands.Open"
    CommandParameter="{Binding RelativeSource={RelativeSource
    FindAncestor, AncestorType={x:Type Expander}},Path=Tag}">X</Button>
    <TextBlock Text="{Binding RelativeSource={RelativeSource
    TemplatedParent},Path=Content}" />
    </StackPanel>
    </DataTemplate>
    </Expander.HeaderTemplate>
    </Expander>
    </Grid>
    </Window>

    private void CommandBinding_Executed (object sender, ExecutedRoutedEventArgs
    e)
    {
    if (e.Parameter == "param1")
    {
    //Your stuff here...
    }
    }

    As you can see the button inside the tempate uses the "Open" command (but
    you can create a custom one) then binds command parameter to Expander's tag
    property so that you can parametrize every expanders with its own "Id".
    The "Id" is then accessible inside command execute method.
    This approach has the advantage that you can easily disable the button
    inside the template simply mapping the CanExecute event inside
    CommandBinding.
    A sample might be:

    private void CommandBinding_CanExecute (object sender,
    CanExecuteRoutedEventArgs e)
    {
    if(e.Parameter=="param1" && dataavailable==true)
    e.CanExecute = true;
    }

    HTH
    --
    Corrado Cavalli [Microsoft .NET MVP-MCP]
    UGIdotNET -
    http://www.ugidotnet.org
    Weblog: http://blogs.ugidotnet.org/corrado/
    Friday, February 22, 2008 8:03 PM
  • By: chutch15


    > "nothing a command"?
    -- 
    - Chad
    
    
    "Corrado Cavalli [MVP]" wrote:
    
    > I think that for your need nothing a command would be the best solution, 
    > here's a snippet:
    > 
    > <Window x:Class="TestWPF.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">
    >  <Window.CommandBindings>
    >   <CommandBinding Command="ApplicationCommands.Open" 
    > Executed="CommandBinding_Executed" />
    >  </Window.CommandBindings>
    >     <Grid>
    >   <StackPanel  Name="stackPanel1" Height="80" VerticalAlignment="Top" />
    >   <Expander Tag="param1" Header="Expander" Height="24.442" 
    > Margin="26.664,0,47.773,99.99" Name="expander1" VerticalAlignment="Bottom">
    >    <Expander.HeaderTemplate>
    >     <DataTemplate>
    >      <StackPanel Orientation="Horizontal">
    >       <Button Command="ApplicationCommands.Open"
    >           CommandParameter="{Binding RelativeSource={RelativeSource 
    > FindAncestor, AncestorType={x:Type Expander}},Path=Tag}">X</Button>
    >       <TextBlock Text="{Binding RelativeSource={RelativeSource 
    > TemplatedParent},Path=Content}" />
    >      </StackPanel>
    >     </DataTemplate>
    >    </Expander.HeaderTemplate>
    >   </Expander>
    >  </Grid>
    > </Window>
    > 
    > private void CommandBinding_Executed (object sender, ExecutedRoutedEventArgs 
    > e)
    >   {
    >    if (e.Parameter == "param1")
    >    {
    >     //Your stuff here...
    >    }
    >   }
    > 
    > As you can see the button inside the tempate uses the "Open" command (but 
    > you can create a custom one) then binds command parameter to Expander's tag 
    > property so that you can parametrize every expanders with its own "Id".
    > The "Id" is then accessible inside command execute method.
    > This approach has the advantage that you can easily disable the button 
    > inside the template simply mapping the CanExecute event inside 
    > CommandBinding.
    > A sample might be:
    > 
    >   private void CommandBinding_CanExecute (object sender, 
    > CanExecuteRoutedEventArgs e)
    >   {
    >    if(e.Parameter=="param1" && dataavailable==true)
    >     e.CanExecute = true;
    >   }
    > 
    > HTH
    > -- 
    > Corrado Cavalli [Microsoft .NET MVP-MCP]
    > UGIdotNET - http://www.ugidotnet.org
    > Weblog: http://blogs.ugidotnet.org/corrado/ 
    > 
    > 
    Friday, February 22, 2008 8:03 PM