Set a ContextMenu UserControl for a Grid - c#

I have created a custom context menu in a separate user Control Class CustomContextMenuUc.
The simple version of the code looks like this.
<ContextMenu x:Class="CustomContextMenu.CustomContextMenuUc"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
ItemsSource="{Binding Groups}">
<ContextMenu.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Items}">
<TextBlock Text="{Binding Description.Value}" />
</HierarchicalDataTemplate>
</ContextMenu.ItemTemplate>
<ContextMenu.OverridesDefaultStyle>True</ContextMenu.OverridesDefaultStyle>
<ContextMenu.Placement>Custom</ContextMenu.Placement>
</ContextMenu>
I want to use this CustomContextMenuUc in multiple Windows. I am able to assign this User control in C# code like
ContextMenuGrid.ContextMenu = new CustomContextMenuUc();
Where ContextMenuGrid is a grid.
I want to do this in XAML How should I do it?
<DataGrid ContextMenu="">

You will have to define the Resource in resources of your window like
<local:CustomContextMenuUc x:Key="MyContextMenu"/>
here local is the xmlns where your contextmenu is defined.
Then you can do
<DataGrid ContextMenu="{StaticResource MyContextMenu}">
Thanks

Related

Bind view model to content control/presenter

on uwp, I want to make a hamburger menu with custom icon in xaml. from the lunch scheduler sample from MS, I try to bind a viewmodel property to a content element in a view like :
<ListBox ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate x:DataType="viewmodels:MenuItem">
<StackPanel Orientation="Horizontal">
<ContentControl Content="{x:Bind Icon2}" />
where Icon is a property like
Icon2 = Application.Current.Resources["BookIcon"],
which works and retreive a viewbox containing a canvas
but I got an exception "Value does not fall within the expected range."
Did some know if it's possible like in WPF ??
nb : if i put the canvas directly in the contentcontrol, it works. perhaps the binding does not accept anything else than string
i found a solution, convert the viewbox to DataTemplate and bind to ContentTemplate
<DataTemplate x:Key="BookIcon">
<Viewbox Width="48" Height="48">
<Canvas Width="24" Height="24">
<Path Data/>
</Canvas>
</Viewbox>
</DataTemplate>
and
<ContentControl ContentTemplate="{x:Bind Icon2}"/>
any other solution is welcome

The XAML code expect my Command in different VM

On the main window xaml, I have a Frame that will host 2 pages, like this
<Window x:Class="Monitor.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" FontSize="14">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="48"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Frame Content="Frame" Grid.Row="1" Source="/Monitor;component/Views/Pages/GroupPage.xaml"/>
</Grid>
</Window>
On the first page (GroupPage.xaml) xaml code is
<Page.DataContext>
<VM:GroupPageVM />
</Page.DataContext>
<Grid>
<ItemsControl x:Name="GroupsOfItemsControl" ItemsSource="{Binding GroupsOfItems}">
<ItemsControl.Template>
<ControlTemplate>
<WrapPanel Width="{TemplateBinding Width}" Height="{TemplateBinding Height}"
FlowDirection="LeftToRight" IsItemsHost="true">
</WrapPanel>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button HorizontalAlignment="Right" Margin="3"
Content="{Binding Name}"
Width="1.2in" Height=".75in" FontSize="14"
Command="{Binding Path=GroupSelectedCommand }"
CommandParameter= "{Binding}"
/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
The GroupPageVM is the VM for this page. GroupsOfItems is a collection of VM for groups. Each Group VM has a Name where I bind it for the content of a button that represent for that group. So on the screen, I will see a collection of buttons in a WrapPanel. Everything display correctly.
Now the issue come with the command that handle the click on the button, the code expect I put my Command (GroupSelectedCommand) and its execute function inside the VM of the Group, instead in the MainWindow VM.
Can someone explain why?
If it is how it suppose to work, how do I put the command in the MainWindow VM? because without that I cannot access to the Frame that host the pages or any NavigationService to go to page 2 by clicking on one of those buttons.
(My simple goal is clicking on a button that represent a group, and it will navigate to a page to display items in that group)
You have bound GroupsOfItems as the ItemsSource of the ItemsControl. Inside GroupsOfItems you have GroupVM objects. So each item inside the ItemsControl will be holding a GroupVM object. Also the DataTemplate defined will be representing the GroupVM object. Thus the Command will be expected inside GroupVM object.
To access parent DataContext, you can try giving ElementName as shown below,
Command="{Binding DataContext.GroupSelectedCommand, ElementName=GroupsOfItemsControl }"
In the above code the button will try to take the Command from the DataContext of ItemsControl which is the GroupPageVM

Why would you use DataTemplate in a ListView?

could someone please explain to me what is the difference if I use
DataTemplate inside the ListView in XAML?
I have used ListView to display the content from my ObservableCollection without using DataTemplate and with DataTemplate it seems to look exactly the same?
Why then would I want to use Data Template? I would like to see a simple explanation/example.
DataTemplate is used to show data in ways beyond a simple text block. Sure doing this:
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
Doesn't buy you much. But it allows you to do stuff like this:
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Source="{Binding ImagePath}"/>
<TextBlock Text="{Caption}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
Which is pretty cool! You can put anything inside the template, so it makes ItemsControl (and its derivatives) some of the most powerful classes in WPF.
Please take a look at #HighCore's answer here WPF MVVM Why use ContentControl + DataTemplate Views rather than straight XAML Window Views?.
<Window x:Class="MyViews.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml
xmlns:viewmodel="clr-namespace:Project.ViewModel"
DataContext="{Binding Main, Source={StaticResource Locator}}"
Title="{Binding Title, Mode=TwoWay}" Height="{Binding Height, Mode=TwoWay}" Width="{Binding Width, Mode=TwoWay}" Left="{Binding Left, Mode=TwoWay}" Top="{Binding Top, Mode=TwoWay}" WindowStartupLocation="CenterScreen">
<Window.Resources>
<HierarchicalDataTemplate DataType="{x:Type viewmodel:OtherViewModel1}">
<ContentPresenter Content="{Binding Path=Text}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type viewmodel:OtherViewModel2}">
<view:ConnectivityLED />
</DataTemplate>
</Window.Resources>
So you can see Mainwindow is talking to more than 1 viewmodel, Main is its own view model which is its datacontext and it also has reference to otherviewmodel1 and otherviewmodel2. Hope this helps, Cheers!

WPF UserControl.Resources reference

I am try to start learning WPF and i am using Telerik.
So i start with simple ComboBox in this article and this is my control:
<telerik:RadComboBox Height="20" Width="200" ItemsSource="{Binding Source={StaticResource DataSource}, Path=Agency}"></telerik:RadComboBox>
What i am trying to do now is to bind an object but first to declare a resource in the XAML (from the article):
<UserControl.Resources>
<example:AgencyViewModel x:Key="DataSource"/> // AgencyViewModel is a class
</UserControl.Resources>
So my problem is that after UserControl i don't have the option Resources, i try to put it inside my control, so i be happy to understand how this is working in WPF
You have to set the DataContext dependency property on a parent control in relation to your ComboBox. The DataContext is then inherited by all (logical-)children. You can then bind to properties on the object referenced by the DataContext dependency property. You do that by referencing the x:Key of your resource with a StaticResource Markup Extension construct.
<UserControl>
<UserControl.Resources>
<example:AgencyViewModel x:Key="DataSource"/> // AgencyViewModel is a class
</UserControl.Resources>
<Grid DataContext="{StaticResource DataSource}">
<telerik:RadComboBox Height="20" Width="200"
ItemsSource="{Binding ItemsCollectionDefinedInViewModel}" />
</Grid>
</UserControl>
You can also do it as it is done in the article without setting the DataContext but instead setting the Source of the binding explicilty.
ItemsSource="{Binding Source={StaticResource DataSource}, Path=Agency}"

WPF Item Templates - TabControl

I am writing an application in which I utilize a tab control which will start with one tab open but allows the user to open multiple other tabs.
Each tab that is openned should have a treeview inside which I fill using databinding when the user loads a file.
I am new to WPF but I feel as if there is a way in which I can create a template containing each of the elements the TabItems should contain. How can I do this using templates? Right now my WPF for the tab items is the following
<TabItem Header="Survey 1">
<TreeView Height="461" Name="treeView1" VerticalAlignment="Top"
Width="625" Margin="0,0,6,0" ItemTemplateSelector="{StaticResource TreeviewDataSelector}" />
</TabItem>
I think you want something like this:
<Window x:Class="TestWpfApplication.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.Resources>
<DataTemplate x:Key="TabItemTemplate">
<TreeView Height="461" Name="treeView1" VerticalAlignment="Top"
Width="625" Margin="0,0,6,0" ItemTemplateSelector="{StaticResource TreeviewDataSelector}" />
</DataTemplate>
</Window.Resources>
<Grid>
<TabControl ItemsSource="{Binding ListThatPowersTheTabs}"
ItemTemplate="{StaticResource TabItemTemplate}">
</TabControl>
</Grid>
You basically create re-usable templates as static resources which you refer to by their key name.
Usually in this sort of situation I bind my TabControl.ItemsSource to a ObservableCollect<ViewModelBase> OpenTabs, so my ViewModel is in charge of adding/removing new tabs as needed.
Then if you want something in every Tab, then overwrite the TabControl.ItemTemplate and use the following line to specify where to display the currently selected TabItem
<ContentControl Content="{Binding }" />
If you don't need to setup something in every single tab, you don't need to overwrite the TabControl.ItemTemplate - it will default to displaying the currently selected ViewModelBase in the Tab.
And I use DataTemplates to specify which View to use
<DataTemplate TargetType="{x:Type local:TabAViewModel}">
<local:TabAView />
</DataTemplate>
<DataTemplate TargetType="{x:Type local:TabBViewModel}">
<local:TabBView />
</DataTemplate>
<DataTemplate TargetType="{x:Type local:TabCViewModel}">
<local:TabCView />
</DataTemplate>

Categories