Assign event handler to ContextMenu item defined in custom style - wpf

I have created Context menu style with predefined MenuItems in it:
<Style TargetType="{x:Type ContextMenu}" x:Key="DataGridColumnFilterContextMenu">
<Setter Property="ContextMenu.Template">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="#868686"
BorderThickness="1"
Background="#FAFAFA">
<Grid MaxHeight="500">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel>
<MenuItem Header="Filtrēt" StaysOpenOnClick="True" Name="DynSearch">
<MenuItem.Icon>
<Image RenderOptions.BitmapScalingMode="NearestNeighbor"
RenderOptions.EdgeMode="Aliased"
Source="/Furniture;component/Resources/search4.png" />
</MenuItem.Icon>
</MenuItem>
<Separator Margin="20 5 20 0" Height="2" Width="Auto" />
<MenuItem Margin="5 5 0 0" StaysOpenOnClick="True" >
<MenuItem.Template>
<ControlTemplate >
<CheckBox Padding="15 0 0 0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >Iezīmēt/Atzīmēt visus</CheckBox>
</ControlTemplate>
</MenuItem.Template>
<MenuItem.Icon>
<Image RenderOptions.BitmapScalingMode="NearestNeighbor"
RenderOptions.EdgeMode="Aliased"
Source="/Furniture;component/Resources/search4.png" />
</MenuItem.Icon>
</MenuItem>
<Separator Margin="20 5 20 0" Height="2" Width="Auto" />
<RadioButton Margin="5 5 0 0" Padding="15 0 0 0" GroupName="Sorting" Content="Augoša secībā" IsChecked="True"/>
<RadioButton Margin="5 5 0 0" Padding="15 0 0 0" GroupName="Sorting" Content="Dilstoša secībā" />
<Separator Margin="20 5 20 5" Height="2" Width="Auto" />
</StackPanel>
<ScrollViewer Grid.Row="1"
Margin="1,0,1,0"
CanContentScroll="True"
VerticalScrollBarVisibility="Auto"
Grid.ColumnSpan="2">
<ItemsPresenter KeyboardNavigation.DirectionalNavigation="Cycle" />
</ScrollViewer>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Now I assigned style to new ContextMenu and wanted to assign Click event handler to MenuItem defined in style with header "Filtrēt". I tried do it like following:
ContextMenu cm = new ContextMenu();
cm.Style = Application.Current.Resources["DataGridColumnFilterContextMenu"] as Style;
var controlList = ((((cm.Template.LoadContent() as Border).Child as Grid).Children)[0] as StackPanel).Children;
MenuItem filterItem = (controlList[0] as MenuItem);
filterItem.Click += MiFiltre_Click;
And when I clicked debugger It didn't come to MiFiltre_Click method. I tried different events like MouseDown and PreviewMouseDown. Also I tried to bound ICommand and this also didn't work.
Then I searched this question and realized that cm.Template.LoadContent() may actually create new instance of my ContextMenu template and I am trying to bound my event handler to different control instance. Then I tried to get ContextMenu node controls with VisualTreeHelper and LogicalTreeHelper and this also didn't work.
So here are questions:
How to achieve Click event handler binding?
If 1st question is too
hard, then how to get ContextMenu childrens current instance defined
in custom style?

I'm going to assume this menu Style is defined in a resource dictionary named "MyResources.xaml".
Create a Class file in the same project folder, and call it "MyResources.xaml.cs".
Make it a partial class like this:
namespace WhateverNamespace
{
public partial class MyResources
{
private void FilterMenuItem_Click(object sender, RoutedEventArgs e)
{
MenuItem mi = (MenuItem)sender;
// Below, PlacementTarget is the control the user right-clicked on.
// Use that directly if you want that instead of its viewmodel.
// This works with the conventional ContextMenu defined below.
//ViewModel viewModel = ((FrameworkElement)((ContextMenu)mi.Parent).PlacementTarget).DataContext as ViewModel;
// This works with your template design. The only difference is
// TemplatedParent instead of Parent.
ViewModel viewModel =
((FrameworkElement)((ContextMenu)mi.TemplatedParent).PlacementTarget)
.DataContext as ViewModel;
MessageBox.Show("FilterMenuItem_Click");
}
}
}
Give the resource dictionary an x:Class attribute:
<ResourceDictionary
x:Class="WhateverNamespace.MyResources"
Wire up the handler:
<MenuItem
Click="FilterMenuItem_Click"
Header="Filtrēt"
StaysOpenOnClick="True"
Name="DynSearch">
By the way, you don't need to mess with styles and templates to create a ContextMenu resource:
<ContextMenu x:Key="DataGridColumnFilterContextMenu">
<MenuItem
Click="FilterMenuItem_Click"
Header="Filtrēt"
StaysOpenOnClick="True"
Name="DynSearch">
<MenuItem.Icon>
<Image
RenderOptions.BitmapScalingMode="NearestNeighbor"
RenderOptions.EdgeMode="Aliased"
Source="/Furniture;component/Resources/search4.png"
/>
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem StaysOpenOnClick="True">
<MenuItem.Header>
<CheckBox>Iezīmēt/Atzīmēt visus</CheckBox>
</MenuItem.Header>
<MenuItem.Icon>
<Image
RenderOptions.BitmapScalingMode="NearestNeighbor"
RenderOptions.EdgeMode="Aliased"
Source="/Furniture;component/Resources/search4.png"
/>
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem>
<MenuItem.Header>
<RadioButton GroupName="Sorting" Content="Augoša secībā" IsChecked="True"/>
</MenuItem.Header>
</MenuItem>
<MenuItem>
<MenuItem.Header>
<RadioButton GroupName="Sorting" Content="Dilstoša secībā" IsChecked="True"/>
</MenuItem.Header>
</MenuItem>
</ContextMenu>

Related

WPF: ContextMenu MenuItem from DataContext/ItemsSource?

I'm building a simple App to store television shows. I have a Video class for the shows with some fields and properties including one reference to an object of type VideoSeason, representing the seasons of TV shows. The corresponding UI element of a Video object is a Button, with a ContextMenu with some actions.
I would like to create a MenuItem inside the ContextMenu, which contains all the seasons added to a TV show represented as submenuitems. I know that to do this, I have to mark the ObservableCollection Seasons as the ItemsSource of the MenuItem Seasons and indicate that any submenuitem inside the MenuItem is bound to the Property SeasonNumber inside VideoSeason.
My problem is that I dont't really know, how to go about binding these submenuitems in XAML, not if this is actually possible. I've already tried some options (e.g., WPF ContextMenu itemtemplate, menuitem inside menuitem, or Binding WPF ContextMenu MenuItem to UserControl Property vs ViewModel Property), but I want only my MenuItem to be bound, not the whole CntextMenu.
Here is the relevant part of the Video class:
public string Name { get; set; }
public int NextEpisode { get; set; }
public ObservableCollection<VideoSeason> Seasons { get; set; }
And here is the relevant part of the XAML code:
<ScrollViewer>
<StackPanel Name="filmHolder"
Grid.Row="1" Grid.Column="0" >
<ItemsControl Name="VideoUIElment">
<ItemsControl.ItemTemplate>
<DataTemplate x:Uid="videoTemplate">
<Border CornerRadius="10" Padding="10, 10" Background="Silver">
<Button Name="filmLabel" Content="{Binding Name}" FontSize="30" Foreground="Black" VerticalAlignment="Center" HorizontalAlignment="Center"
Click="FilmLabel_Click" BorderThickness="0">
<Button.ContextMenu>
<ContextMenu Name="LocalMenu">
<MenuItem Header="Rename"/>
<MenuItem Header="Delete"/>
<MenuItem Header="Add New Season" Name="NewSeason" Click="NewSeason_Click"/>
<MenuItem Header="Seasons" ItemsSource="{Binding Seasons}">
<!--<MenuItem.ItemTemplate This is one of the things I tried in vain>
<DataTemplate>
<MenuItem Header="{Binding SeasonNumber}"/>
</DataTemplate>
</MenuItem.ItemTemplate>-->
</MenuItem>
</ContextMenu>
</Button.ContextMenu>
</Button>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
As it can be seen, the problematic part is nested in the DataTemplate belonging to the UI of the Video, which might be the cause of the problem, but I'm not sure.
If you bind the ItemsSource property of the ItemsControl to an IEnumerable<Video>, this should work:
<ItemsControl Name="VideoUIElment" ItemsSource="{Binding Videos}">
<ItemsControl.ItemTemplate>
<DataTemplate x:Uid="videoTemplate">
<Border CornerRadius="10" Padding="10, 10" Background="Silver">
<Button Name="filmLabel" Content="{Binding Name}" FontSize="30" Foreground="Black" VerticalAlignment="Center" HorizontalAlignment="Center"
Click="FilmLabel_Click" BorderThickness="0">
<Button.ContextMenu>
<ContextMenu Name="LocalMenu">
<MenuItem Header="Rename"/>
<MenuItem Header="Delete"/>
<MenuItem Header="Add New Season" Name="NewSeason" Click="NewSeason_Click"/>
<MenuItem Header="Seasons" ItemsSource="{Binding Seasons}">
<MenuItem.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding SeasonNumber}"/>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</ContextMenu>
</Button.ContextMenu>
</Button>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Make sure that SeasonNumber is a public property of the VideoSeason class.

How to bind a button icon to the selected item of a drop-down list?

In a WPF application and using the Fluent Ribbon Control Suite, I have a DropDownButton that opens up a Gallery that lets the user select a color.
Here is the XAML that creates the button:
<Fluent:DropDownButton x:Name="btnCommentColor" Header="Comments">
<Fluent:DropDownButton.Icon>
<!-- What goes here? -->
</Fluent:DropDownButton.Icon>
<Fluent:Gallery x:Name="galCommentColor" ItemsSource="{Binding Source={StaticResource colorPropertiesOdp}}" SelectedValuePath="Name" MaxItemsInRow="12">
<Fluent:Gallery.ItemTemplate>
<DataTemplate>
<Border BorderThickness="1" CornerRadius="2" BorderBrush="Black" Width="25" Height="25" VerticalAlignment="Stretch" Background="{Binding Name}" />
</DataTemplate>
</Fluent:Gallery.ItemTemplate>
</Fluent:Gallery>
</Fluent:DropDownButton>
The SelectedItem of the Gallery returns the Name of the color. I want to make the Icon of the button display the actual color that was selected. Can this be done purely with XAML? I have been trying various things found online but so far have been unable to get anything other than the color name to appear where I want the color rectangle to go. Look for the "What Goes Here?" in the XAML above.
I appreciate any helpful suggestions. Thanks for reading!
UPDATE:
I tried the answer given below and it still doesn't work. I must have something wrong. Here's an updated listing of all the XAML code for this button. Take a look at the XAML for the Gallery itself and the binding for the SolidColorBrush and tell me if you see what i've done wrong.
<Window.Resources>
<ObjectDataProvider MethodName="GetType"
ObjectType="{x:Type sys:Type}" x:Key="colorsTypeOdp">
<ObjectDataProvider.MethodParameters>
<sys:String>System.Windows.Media.Colors, PresentationCore,
Version=3.0.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35</sys:String>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<ObjectDataProvider ObjectInstance="{StaticResource colorsTypeOdp}"
MethodName="GetProperties" x:Key="colorPropertiesOdp">
</ObjectDataProvider>
</Window.Resources>
<Fluent:DropDownButton Name="btnCommentColor" Header="Comments">
<Fluent:DropDownButton.LargeIcon>
<Grid Width="32" Height="32">
<Image Source="Icons\BlueLarge.png" />
<Border Height="32" VerticalAlignment="Bottom" BorderThickness="0" CornerRadius="2">
<Border.Background>
<SolidColorBrush Color="{Binding ElementName=galCommentColor, Path=SelectedValue, FallbackValue=Green}" />
</Border.Background>
</Border>
</Grid>
</Fluent:DropDownButton.LargeIcon>
<Fluent:Gallery Name="galCommentColor" ItemsSource="{Binding Source={StaticResource colorPropertiesOdp}}" SelectedValuePath="Name" MaxItemsInRow="12">
<Fluent:Gallery.ItemTemplate>
<DataTemplate>
<Border ToolTip="{Binding Path=Name}" BorderThickness="1" CornerRadius="2" BorderBrush="Black" Width="25" Height="25" VerticalAlignment="Stretch" Background="{Binding Name}" />
</DataTemplate>
</Fluent:Gallery.ItemTemplate>
</Fluent:Gallery>
</Fluent:DropDownButton>
On the page 17 of the walkthrough you have an example of what you are trying to achieve.
You can download it here : http://fluent.codeplex.com/documentation
Taken from the walkthrough :
<fluent1:Ribbon>
<fluent1:Ribbon.Menu>
<fluent1:Backstage />
</fluent1:Ribbon.Menu>
<fluent1:RibbonTabItem Header="Home">
<fluent1:RibbonGroupBox Header="Clipboard">
<!-- The following code shows standard mode for color gallery -->
<fluent1:DropDownButton Header="Standard">
<!-- It's possible to create custom icon to present selected color -->
<fluent1:DropDownButton.Icon>
<Grid Width="16" Height="16">
<Image Source="Images\FontColor.png" />
<Border Height="4"
VerticalAlignment="Bottom"
BorderThickness="0">
<Border.Background>
<SolidColorBrush
Color="{Binding ElementName=ColorGalleryStandard, Path=SelectedColor, FallbackValue=Black}" />
</Border.Background>
</Border>
</Grid>
</fluent1:DropDownButton.Icon>
<fluent1:ColorGallery x:Name="ColorGalleryStandard"
IsNoColorButtonVisible="False"
SelectedColor="Red" />
<fluent1:MenuItem Header="A Menu Item" Icon="Images\Pink.png" />
</fluent1:DropDownButton>
</fluent1:RibbonGroupBox>
</fluent1:RibbonTabItem>
</fluent1:Ribbon>
UPDATE
I see nothing wrong in your code, I've pasted it and it ran successfully, here is it again pasted from my working test.
<Window x:Class="WpfApplication8.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:fluent1="clr-namespace:Fluent;assembly=Fluent"
xmlns:system="clr-namespace:System;assembly=mscorlib"
Title="MainWindow"
Width="525"
Height="350">
<Window.Resources>
<ObjectDataProvider x:Key="colorsTypeOdp"
MethodName="GetType"
ObjectType="{x:Type system:Type}">
<ObjectDataProvider.MethodParameters>
<system:String>
System.Windows.Media.Colors, PresentationCore,
Version=3.0.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35
</system:String>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<ObjectDataProvider x:Key="colorPropertiesOdp"
MethodName="GetProperties"
ObjectInstance="{StaticResource colorsTypeOdp}" />
</Window.Resources>
<fluent1:DropDownButton Name="btnCommentColor" Header="Comments">
<fluent1:DropDownButton.LargeIcon>
<Grid Width="32" Height="32">
<Image Source="Icons\BlueLarge.png" />
<Border Height="32"
VerticalAlignment="Bottom"
BorderThickness="0"
CornerRadius="2">
<Border.Background>
<SolidColorBrush
Color="{Binding ElementName=galCommentColor, Path=SelectedValue, FallbackValue=Green}" />
</Border.Background>
</Border>
</Grid>
</fluent1:DropDownButton.LargeIcon>
<fluent1:Gallery Name="galCommentColor"
ItemsSource="{Binding Source={StaticResource colorPropertiesOdp}}"
MaxItemsInRow="12"
SelectedValuePath="Name">
<fluent1:Gallery.ItemTemplate>
<DataTemplate>
<Border Width="25"
Height="25"
VerticalAlignment="Stretch"
Background="{Binding Name}"
BorderBrush="Black"
BorderThickness="1"
CornerRadius="2"
ToolTip="{Binding Path=Name}" />
</DataTemplate>
</fluent1:Gallery.ItemTemplate>
</fluent1:Gallery>
</fluent1:DropDownButton>
</Window>
Thanks to Aybe for confirming that it's not me. I did get the result I wanted by using a converter on the LargeIcon property of the DropDownButton.
Here's the XAML:
<Fluent:DropDownButton Name="btnCommentColor" Header="Comments" HasTriangle="False" LargeIcon="{Binding ElementName=galCommentColor, Path=SelectedValue, Converter={StaticResource ColorNameToBorderConverter_Key}}">
<Fluent:Gallery x:Name="galCommentColor" ItemsSource="{Binding Source={StaticResource colorPropertiesOdp}}" SelectedValuePath="Name" MaxItemsInRow="12" SelectedIndex="51">
<Fluent:Gallery.ItemTemplate>
<DataTemplate>
<Border ToolTip="{Binding Name}" BorderThickness="1" CornerRadius="2" BorderBrush="Black" Width="25" Height="25" VerticalAlignment="Stretch" Background="{Binding Name}" />
</DataTemplate>
</Fluent:Gallery.ItemTemplate>
</Fluent:Gallery>
</Fluent:DropDownButton>
And the code:
Public Class ColorNameToBorderConverter
Implements IValueConverter
Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As Globalization.CultureInfo) As Object Implements IValueConverter.Convert
If ApplicationIsInDesignMode Then value = "Black"
If TypeOf value Is String Then
Return New Border With {
.Height = 32,
.BorderThickness = New Thickness(1),
.BorderBrush = New SolidColorBrush(System.Windows.Media.Colors.Black),
.CornerRadius = New CornerRadius(2, 2, 2, 2),
.VerticalAlignment = VerticalAlignment.Bottom,
.Background = New SolidColorBrush(ColorConverter.ConvertFromString(value))
}
Else
Throw New InvalidOperationException("Unsupported type [" & value.GetType.ToString & "]")
End If
End Function
Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
Throw New NotImplementedException
End Function
Private Shared ReadOnly Property ApplicationIsInDesignMode() As Boolean
Get
Return CBool(DesignerProperties.IsInDesignModeProperty.GetMetadata(GetType(DependencyObject)).DefaultValue)
End Get
End Property
End Class

WPF button with drop down list and arrow

Can someone suggest the best way to have a button with an arrow and dropdown list like in visual studio toolbar button new item. As you can find in VS the mouse hover is highlighting both default button and arrow button and after selecting an item from list the default button is changing according your selection.
Here is a piece of code which is showing drop down menu, but not for full functionality described above:
<StackPanel Orientation="Horizontal">
<Border CornerRadius="0" BorderBrush="Black" BorderThickness="1">
<Button Name="CreateButton" Click="CreateButton_Click" Background="Transparent" BorderBrush="{x:Null}">
<Image Source="/OMS.Resources;component/Resources/Images/LibraryImages/add1.png" />
<Button.ContextMenu>
<ContextMenu HorizontalAlignment="Right">
<MenuItem Header=" doc" Click="CreateDocButton_Click">
<MenuItem.Icon>
<Image Source="/OMS.Resources;component/Resources/Images/LibraryImages/add_sheet.png" Width="24" Height="24" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header=" xls" Click="CreateXlsButton_Click">
<MenuItem.Icon>
<Image Source="/OMS.Resources;component/Resources/Images/LibraryImages/add_sheet.png" Width="24" Height="24" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header=" txt" Click="CreateTxtButton_Click">
<MenuItem.Icon>
<Image Source="/OMS.Resources;component/Resources/Images/LibraryImages/add_sheet.png" Width="24" Height="24" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</Button.ContextMenu>
</Button>
</Border>
<Border CornerRadius="0" BorderBrush="Black" BorderThickness="1">
<Button HorizontalAlignment="Left" VerticalAlignment="Stretch" Background="Transparent" BorderBrush="{x:Null}"
ContextMenuService.IsEnabled="False" Click="AddButtonContextMenu_Click">
<Image Source="/OMS.Resources;component/Resources/Images/LibraryImages/arrow_down.png" VerticalAlignment="Center" Width="9" />
</Button>
</Border>
</StackPanel>
The solution is to make use a menu item and decorate it.
XAML Code:
<MenuItem Click="AddPresetButton_Click" x:Name="AddPresetButton">
<MenuItem.Icon>
<Image Source="/MyApp.Application;component/Resources/add.png" Height="20"/>
</MenuItem.Icon>
<MenuItem.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Add Preset"/>
<Image Source="/MyApp.Application;component/Resources/arrow_down_simple.png"
Height="10" Margin="2,0,0,0"/>
</StackPanel>
</MenuItem.Header>
<MenuItem.ContextMenu>
<ContextMenu>
<MenuItem Header="Add 1"/>
<MenuItem Header="Add 2"/>
<MenuItem Header="Add 3"/>
</ContextMenu>
</MenuItem.ContextMenu>
</MenuItem>
C# Code:
When the menu is pressed the context menu is opened.
private void AddPresetButton_Click(object sender, RoutedEventArgs e)
{
var addButton = sender as FrameworkElement;
if (addButton != null)
{
addButton.ContextMenu.IsOpen = true;
}
}
It looks like you have three problems to solve:
Styling / Layout
Highlight dropdown and button OnMouseOver
Change default button according to menu's last selection
Styling / Layout
Here are a couple of examples:
http://dvoituron.wordpress.com/2011/01/06/toolbar-dropdownbutton-in-wpf/
http://blogs.msdn.com/b/llobo/archive/2006/10/25/split-button-in-wpf.aspx
I am sure there are many other ways (e.g. using a plain button and ComboBox styled appropriately)
Highlighting dropdown and button OnMouseOver
Experiment with triggers; e.g:
WPF Mouseover Trigger Effect for Child Controls
WPF - How to change children's style on mouseover of parent
Change default button according to menu's last selection
Try the MVVM approach:
The button element will be bound to a property on your ViewModel. Each menu item will call an action (ICommand) in your ViewModel. This ViewModel will know which menu item was called, and update the button's property on the ViewModel. The button will automatically update using data binding.

How do I access elements that have been dynamically assigned to a control in the form of an XAML resource?

I have the following resource in my window that declares how a certain kind of TabItem should look like.
<Window.Resources>
<StackPanel x:Key="TabSearchContents" x:Shared="False"
Orientation="Vertical">
<Border
BorderThickness="3"
BorderBrush="Purple">
<TextBlock
Text="SEARCH BOOKS"
FontFamily="Verdana"
FontSize="25"
Foreground="Blue"
HorizontalAlignment="Center" />
</Border>
<StackPanel
Height="30"
Orientation="Horizontal"
Margin="5">
<TextBox
x:Name="txtSearch"
Width="650"
FontFamily="Comic Sans MS"
Foreground="Chocolate" />
<Button
x:Name="btnSearch"
Width="100"
Content="Go!"
Click="BtnSearch_Click" />
</StackPanel>
<Grid x:Name="gridResults">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="450"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ScrollViewer Grid.Column="0" VerticalScrollBarVisibility="Auto">
<ItemsControl x:Name="itmsSearch" ItemsSource="{Binding}" Padding="4"
ItemTemplate="{StaticResource SearchResultItemDT}">
</ItemsControl>
</ScrollViewer>
<StackPanel x:Name="stkpnlDetails">
</StackPanel>
</Grid>
</StackPanel>
</Window.Resources>
Then, in my code-behind, I dynamically create a tab and assign to the TabControl that is already present in my window.
void BtnNewTab_Click(object sender, RoutedEventArgs e)
{
TabItem tb = new TabItem();
tb.Content = this.Resources["TabSearchContents"];
tb.DataContext = _bridge.SearchBooksByTitle("e");
tb.Header = "Wuttp yo!";
Button btnGo = ((Button)tb.FindName("btnSearch"));
ItemsControl i = (ItemsControl)tb.FindName("itmsSearch");
btnGo.Resources.Add("ResultList", i);
daTabs.Items.Add(tb);
tb.Focus();
}
I want to access the btnSearch Button that is declared in my XAML resource.
As it is, this code throws an exception since btnGo turns out to be null (as well as i) since it can't find the expected control via FindName().
I read about the RegisterName() method, but it requires a reference to an instance of the required control... which I don't have.
I dont think you should define your button like this, try defining it in a style, creating a button and assigning the button that style, i think you will be able to get what you are going for this way.
myTheme.xaml
<ResourceDictionary
<Style x:Key="btnSearch" TargetType="{x:Type Button}">
<Setter Property="Width" Value="100"/>
<Setter Property="Content" Value="Go!"/>
<Setter Property="Click" Value="btn_Click"/>
</Style>
ResourceDictionary/>
myCode.cs
Button btnGo = new Button;
btnGo.Style = "{DynamicResource btnSearch}";
Hope this helps,
Eamonn

Bind Command for MenuItem in DataTemplate without Tag

I want to bind a command in my ViewModel to a menuItem which is in DataTemplate. I can do that with using Tag. Is there any method which can do the same task but without using tag.
<Window.Resources>
<DataTemplate x:Key="StudentListBoxItemTemplate">
<StackPanel Tag="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}}">
<TextBlock Text="{Binding Name}"/>
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Header="Trigger" Command="{Binding PlacementTarget.Tag.TriggerCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
</ContextMenu>
</StackPanel.ContextMenu>
</StackPanel>
</DataTemplate>
</Window.Resources>
<StackPanel>
<ListBox
ItemsSource="{Binding StudentList}"
ItemTemplate="{StaticResource StudentListBoxItemTemplate}">
</ListBox>
</StackPanel>
My ViewModel
public class MainViewModel {
public ICommand TriggerCommand { ... }
public ObservableList<Student> StudentList { ... }
}
With your current design, you need to get from the ContextMenu through the StackPanel and back to the DataContext of the containing ListBox. What makes this awkward is that the DataContext of the StackPanel is already narrowed down to a particular student.
There are at least two ways to make this easier:
Provide a TriggerCommand property in Student so the command is right there where you need it
Provide a Parent property in the Student to escape the narrowed scope
You can try to add click event to the menuItem like following
<Menu Style="{StaticResource bellRingersFontStyle}" Height="23" Name="menu1" Width="Auto" DockPanel.Dock="Top" VerticalAlignment="Top">
<MenuItem Header="_File">
<MenuItem Header="_New Member" Name="newMember" Click="newMember_Click" >
<MenuItem.Icon>
<Image Source="Face.bmp" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="_Save Member Details" Name="saveMember" IsEnabled="False" Click="saveMember_Click">
<MenuItem.Icon>
<Image Source="Note.bmp" />
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="E_xit" Name="exit" Click="exit_Click" />
</MenuItem>
<MenuItem Header="_Help">
<MenuItem Header="_About Middleshire Bell Ringers" Name="about" Click="about_Click" >
<MenuItem.Icon>
<Image Source="Ring.bmp" />
</MenuItem.Icon>
</MenuItem>
</MenuItem>
</Menu>
Try binding the command to Click. My VS is down so can't check at this moment.
One way would be to get the context menu collection presentation defined in your viewmodel, which will contain the header string and command action (maybe with predicate).
The viewmodel creates an observable collection of the contextmenu items and the view binds that to ContextMenu itemssource and sets the displaymember path to header string.

Resources