Control Template used in Code Behind to add dynamically to a StackPanel - c#

I have the following ControlTemplate in my /Themes/Generic.xaml:
<ControlTemplate x:Key="GroupList" TargetType="{x:Type Expander}">
<Expander IsExpanded="True">
<Expander.Header>
<Label Style="{DynamicResource headline3}" FontWeight="Light" BorderThickness="0 0 0 1">TEst group</Label>
</Expander.Header>
<ListBox x:Name="lstBoxContacts" Height="auto" BorderThickness="0" VerticalAlignment="Top" AllowDrop="True" PreviewDragOver="lstBoxContacts_PreviewDragOver" Drop="lstBoxContacts_Drop" ItemsSource="{Binding Contacts}">
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Fullname}" />
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ContextMenu>
<ContextMenu >
<MenuItem Header="Send Message" Command="local:CustomCommands.cmdMessageWrite"/>
<MenuItem Header="Show Details" Command="local:CustomCommands.cmdContactDetails"/>
<MenuItem Header="Delete" Command="local:CustomCommands.cmdContactDelete"/>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
</Expander>
</ControlTemplate>
This should give me an expander with a Listbox in it. To which I can pass a ObservableCollection<User> object, that is than being displayed (Only the Fullname attribute of the user).
In my MainWindow.xaml I have a Stackpanel named "stkpContactsAndGroups" to which I would like to add Expanders programmatically and dynamically.
The contents come from an API call (Which all works).
From what I have seen I should work with Template.FindName. However, when I supply the origin where said template can be found VS bucks me.
Expander grp = (Expander)Template.FindName("GroupList", new Generic());
stkpContactsAndGroups.Children.Add(grp);
It cannot convert Generic() to FrameworkElement. Am I missing the point here?

FrameworkTemplate.FindName method helps you to find an element inside template if this template is applied to templated parent. In you case you'd better use the Application.FindResource method, create new Expander, apply template using the Expander.Template property and after this add this expander to the StackPanel.Children collection

Related

Access Data Context properties from Context Menu

I try to learn a little bit about how a listboxview combined with context menu works, so I've made the following XAML code:
<UserControl>
...
<ListView
x:Name="level1Lister"
Grid.Row="1"
behaviours:AutoScrollListViewBehaviour.ScrollOnNewItem="True"
ItemsSource="{Binding LogBuffer, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Padding" Value="1" />
<Setter Property="Margin" Value="2,0" />
<Setter Property="BorderThickness" Value="0" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Command="{Binding Path=DataContext.ValidateAllCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}, FallbackValue=9999999999}" Header="Copy" />
</ContextMenu>
</StackPanel.ContextMenu>
<TextBlock Foreground="{Binding Color, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Text="{Binding Message, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</UserControl>
My main problem is that I'm not able to access my "ValidateAllCommand" function, for some reason... I think it has to do with the "RelativeSource={RelativeSource FindAncestor ... " part but I can't figure out how.
The "problem" with the ContextMenu is that it is not part of the visual tree. The reason is that the MenuItem uses a Popup to host the content. While the Popup itself is part of the visual tree, its Child content is disconnected as it is rendered in a new Window instance. We know there can can only be a single Window in the tree and this Window must be the root.
Since Binding.RelativeSource traverses the visual tree, starting within the detached tree of the Popup, to find the binding source, the Bindig does not resolve.
The Popup.Child content inherits the DataContext of the Popup. In case of the ContextMenu it means that the MenuItem inherits the DataContext from the ContextMenu or from the parent of the ContextMenu, to be more precise. In your scenario the DataContext is the data model of the ListBoxItem i.e. the DataContext of the DataTemplate.
This means, one solution could be to implement the command in the item model.
The second solution is to make use of routed commands. This solution might be more reasonable in your scenario. The routed command/event can cross the boundary between the two trees.
In the following example, I use the ApplicationCommands.Copy command, one of the predefined routed commands. The MenuItem.Parameter is bound to the DataContext, which is the item data model (inherited from the ContextMenu, as mentioned before). This way the command handler can know the data source.
The event handler can be attached to any parent element using the UIElement.CommandBindings property. In the example the handler is attached to the ListBox element:
MainWindow.xaml
<ListBox>
<ListBox.CommandBindings>
<CommandBinding Command="{x:Static ApplicationCommands.Copy}"
Executed="CopyCommand_Executed" />
</ListBox.CommandBindings>
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type local:DataItem}">
<StackPanel>
<StackPanel.ContextMenu>
<ContextMenu>
<!--
Here we are in a detached visual tree.
The DataContext is inherited from the ContextMenu element.
The framework allows routed events to cross the boundaries between the trees.
-->
<MenuItem Command="{x:Static ApplicationCommands.Copy}"
CommandParameter="{Binding}"
Header="Copy" />
</ContextMenu>
</StackPanel.ContextMenu>
<TextBlock />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
MainWindow.xaml.cs
private void CopyCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
var listBoxItemModel = e.Parameter as LogBufferItem;
// TODO::Handle Copy command. For example:
var commandTarget = this.DataContext as ICommandModel;
commandTarget.ValidateAllCommand.Execute(listBoxItemModel);
}
A third, but not recommended solution is to bind the DataContext of the ContextMenu parent to the context of interest:
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type local:DataItem}">
<StackPanel>
<!-- DataTemplate DataContext -->
<Grid DataContext="{Binding RelativeSource={RelativeSource AncestorType=ListBox}, Path=DataContext}">
<!-- ListBox DataContext -->
<Grid.ContextMenu>
<ContextMenu>
<!--
Here we are in a detached visual tree.
The DataContext is inherited from the ContextMenu/Grid element.
-->
<MenuItem Command="{Binding ValidateAllCommand}"
Header="Copy" />
</ContextMenu>
</Grid.ContextMenu>
</Grid>
<!-- DataTemplate DataContext -->
<TextBlock />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox

ProgressBar into ListBoxItem becomes unselectable while performing steps in XAML

I have ListBox dynamically generated. Each item contains a ProgressBar which I can stop, pause or resume by the means of clicking the relative voice on its ContextMenu.
Here is the XAML code:
<ListBox Grid.Column="1" Name="TransfersList" Margin="30,10,-0.444,34.889" ItemsSource="{Binding DataTx}"
SelectionChanged="TransfersList_SelectionChanged" Grid.Row="1" Grid.ColumnSpan="3"
HorizontalContentAlignment="Stretch">
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Ottieni informazioni" Click="GetInfo" />
<MenuItem Header="Metti in pausa" Click="PauseTransfer" />
<MenuItem Header="Riprendi trasferimento" Click="ResumeTransfer" />
<MenuItem Header="Annulla trasferimento" Click="StopTransfer" />
</ContextMenu>
</ListBox.ContextMenu>
<ListBox.ItemTemplate>
<DataTemplate>
<ProgressBar Height="20" Minimum="0" Maximum="{Binding NChunks}" Name="gasparino_il_carbonaro"
Value="{Binding PbStatus}" Foreground="{Binding Color}" ToolTip="{Binding TooltipInfo}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
When I click, for example, the ContextMenu "GetInfo" voice, I get always the '-1' SelectedIndex (even if I have many bars).
I suppose that the problem is due to the ProgressBar filling (performing steps and so XAML refreshing), when the ProgressBar is filling/refreshing the "system" becomes unable to understand the selected index (which bar I selected with right click?).
My concise question is:
How can I bind the right-clicked bar with the ContextMenuItem relative
method?

How to show context menu on a ListBoxItem template on a grid when right-clicking outside the contents of it?

Hello,
I have a ListBox.ItemTemplate (containing obviously a DataTemplate) containing a Grid which itself contains a Label.
The Grid has a Grid.ContextMenu. The issue is that the ContextMenu only shows when I right click on the Label. Not outside.
Here is an image to show the problem if you didn't understand (But I am sure you did ;) !)
Here is the code :
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0,0,0,1">
<Grid.ContextMenu>
<ContextMenu>
<MenuItem CommandParameter="{Binding}" Command="{Binding Source={x:Reference window}, Path=DataContext.DeleteCommand}" Header="Supprimer">
<MenuItem.Icon>
<Image Source="/SchoolTools.Teacher;component/Images/DeleteIcon.png" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</Grid.ContextMenu>
<Label Content="{Binding ClassYear}" Padding="0"
HorizontalAlignment="Center" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
Thanks !
Change the label's HorizontalAlignment to Stretch so that it fills the entire grid cell.

How to display a button or label when right clicking on a ListBox item

I'm new to WPF and MVVM, I've build few things, and now tryining to display a delete "button" when user right clicks on a ListBox item.
My listbox looks like this righ now
<ListBox DisplayMemberPath="QUERYNAME"
SelectedValuePath="USERQUERYID"
ItemsSource="{Binding RS.SavedQueryList, UpdateSourceTrigger=PropertyChanged}"
SelectedValue="{Binding RS.SelectedValue, UpdateSourceTrigger=PropertyChanged}"
Height="300" HorizontalAlignment="Left" Name="listBox2" VerticalAlignment="Top" Width="101" Margin="521,74,0,0" TabIndex="0">
Thanks
You can add the button and label (in fact, any element you want) to a ContextMenu and assign that ContextMenu to ListBoxItems. For example, in my Window, I'll have something like this:
<Window.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock Text="Delete This Item" Margin="10"/>
<Button Content="Delete"/>
</StackPanel>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
This will use the power of Styles to apply a customized ContextMenu to all ListBoxItems the window. After that you can bind Button.Command to your ViewModel.
<ListBox DisplayMemberPath="QUERYNAME"
SelectedValuePath="USERQUERYID"
ItemsSource="{Binding RS.SavedQueryList, UpdateSourceTrigger=PropertyChanged}"
SelectedValue="{Binding RS.SelectedValue, UpdateSourceTrigger=PropertyChanged}"
Height="300" HorizontalAlignment="Left" Name="listBox1" VerticalAlignment="Top" Width="101" Margin="521,74,0,0" TabIndex="0">
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Delete">
<MenuItem.Icon>
<Image Width="16" Height="16" Source="pack://application:,,,/Img/Delete.png" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
<ListBox.ContextMenu>
</ListBox>
Obviously you'll need to use some Command with your MenuItem...
you can do that by using the MVVM light behavior EventToCommand see http://msdn.microsoft.com/en-us/magazine/dn237302.aspx
Set the event to MouseRightButtonUp

Is it possible to make a binding in a menu control using list?

I made a style for my menu control and now I want to use that style for all the menuItems, but with different text in the textbox. I was wondering if I can use a List to populate the binding element...I tried but it doesnt work...Have I missed something or I have to use something else?
List<string> itemArray = new List<string>();
itemArray.Add("label1");
itemArray.Add("label2");
itemArray.Add("label3");
Binding binding = new Binding();
binding.Path = new PropertyPath("itemArray");
this.menu1.SetBinding(TextBox.TextProperty, binding);
and the one part of the style is, if it helps...:
<Setter.Value>
<ControlTemplate TargetType="{x:Type MenuItem}">
<Grid>
<Border Name="MainBorder" BorderThickness="2,2,2,0" >
<Grid>
<TextBlock Text="{Binding Path=itemArray}" Margin="30,10,0,0" FontFamily="Arial" FontSize="14" FontWeight="Bold" />
<Image Width="15" Height="15" Source="image.PNG" Margin="-100,0,0,0" />
</Grid>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
You're trying to bind a text element to a List<T>, which will result in the type name. If you want the menu to populate itself from a list of objects, consider binding the menu's ItemsSource property to that list:
<Menu ItemsSource="{Binding ListOfItems}">
<Menu.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding Text}" Command="{Binding Command}" />
</DataTemplate>
</Menu.ItemTemplate>
</Menu>
In this example, each list item is an object with a Text property which shows up as the display string, and a Command property, which is an object that implements ICommand. When the user chooses a menu item, that list item's Command.Execute method is invoked; you could use something like RelayCommand or ReactiveCommand to turn that into a method call.
This allows for a flat menu; for a hierarchical menu you'll have to do something a little different.

Categories

Resources