TreeView's contextmenu appears on the topnode instead of TreeViewItem's - c#

I've got a problem I couldn't find solution for. Here is my TreeView's XAML:
<TreeView ItemsSource="{Binding Parents}" ContextMenu="{StaticResource TreeViewContextMenu}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Image}" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
And I've also got this style in my ContentControl.Resources:
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
<Setter Property="ContextMenu" Value="{StaticResource TreeViewItemContextMenu}" />
</Style>
When I rightclick any item apart from the very first one, the TreeViewItemContextMenu appears, just as intended.
But the issue is when I rightclick the topnode, the TreeViewContextMenu appears instead.
I tried to do without the TreeViewContextMenu at all, but then the topnode had no ContextMenu either.
I would be very glad to know, what and where I missed. Thanks in advance.

I'm trying using your xaml code. This is works.
<Window x:Class="WpfApplication1.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">
<Window.Resources>
<ContextMenu x:Key="TreeViewItemContextMenu">
<MenuItem Header="Item Menu"/>
</ContextMenu>
<ContextMenu x:Key="TreeViewContextMenu">
<MenuItem Header="Tree Menu"/>
</ContextMenu>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
<Setter Property="ContextMenu" Value="{StaticResource TreeViewItemContextMenu}" />
</Style>
</Window.Resources>
<Grid>
<TreeView ItemsSource="{Binding Parents}" ContextMenu="{StaticResource TreeViewContextMenu}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Image}" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
</Window>
In code
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Parents = new ObservableCollection<MyTreeItem>();
var children=new ObservableCollection<MyTreeItem>();
children.Add(new MyTreeItem(){ Name="child1"});
children.Add(new MyTreeItem(){ Name="child2"});
Parents.Add(new MyTreeItem() { Name = "Parent Node", Children = children });
this.DataContext = this;
}
public ObservableCollection<MyTreeItem> Parents { get; set; }
}
public class MyTreeItem {
public string Name { get; set; }
public ObservableCollection<MyTreeItem> Children { get; set; }
}
When i click on the top node and child node, Context menu is same

Related

WPF binding to UserControl from Page

I have a UWP application, and it has a page containing a few textbox controls bound to A.B.C[2].D, A.B.C[2].E, A.B.C[2].F, and so on
Now I want to move the text boxes to a separate UserControl to simplify my page's XAML but
still want them to be bound to A.B.C[2].D, A.B.C[2].E etc.
How can I achieve this?
Thank you
Here is my UserControl
<UserControl
x:Class="Inspecto.HWStatusDisplayControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Truphase360"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="250">
<Grid>
<Grid.Resources>
<Style x:Key="styleTxtBox" TargetType="TextBox">
<Setter Property="IsReadOnly" Value="True" />
<Setter Property="Width" Value="75"/>
</Style>
<Style x:Key="styleTxtBlk" TargetType="TextBlock">
<Setter Property="FontSize" Value="12" />
<Setter Property="Margin" Value="10"/>
<Setter Property="Width" Value="75" />
</Style>
</Grid.Resources>
<StackPanel Orientation="Vertical" Margin="20">
<TextBlock Text="{x:Bind Header}" FontSize="16" FontWeight="Bold" Margin="10">
</TextBlock>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Temp1" Style="{StaticResource styleTxtBlk}" />
<TextBox Style="{StaticResource styleTxtBox}" Text="{Binding Mode=OneWay, Path=Temperature1 }" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Temp2" Style="{StaticResource styleTxtBlk}" />
<TextBox Style="{StaticResource styleTxtBox}" Text="{Binding Mode=OneWay, Path=Temperature2 }" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="SW Version" Style="{StaticResource styleTxtBlk}" />
<TextBox Style="{StaticResource styleTxtBox}" Text="{Binding Mode=OneWay, Path=SWVersionStr }" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="HW Version" Style="{StaticResource styleTxtBlk}" />
<TextBox Style="{StaticResource styleTxtBox}" Text="{Binding Mode=OneWay, Path=HWVersionStr }" />
</StackPanel>
</StackPanel>
</Grid>
</UserControl>
The code block directly under exists inside a page and is repeated multiple times.
The TextBoxs' were bound to {x:Bind ViewModel.DeviceData.Status.Devices[0].Temperature1 } and so on inside the Page
Now I want to display the same from UserControl.
In the data object, the instance of Devices[] is replaced every few seconds. The new array was created by deserializing from JSON object and directly assigned to like
ViewModel.DeviceData.Status.Devices = FromJSON(string);
Thank you
I agree with #EldHasp. The solution for your scenario is that you might need to create a custom dependency property in the UserControl. Then pass the A.B.C object to this property of the UserControl via binding.
I've made a simple demo about this, you could take look at the code and adjust it for your scenario.
UserControl XAML
<Grid x:Name="MyGrid">
<Grid.Resources>
<Style x:Key="styleTxtBox" TargetType="TextBox">
<Setter Property="IsReadOnly" Value="True" />
<Setter Property="Width" Value="75"/>
</Style>
<Style x:Key="styleTxtBlk" TargetType="TextBlock">
<Setter Property="FontSize" Value="12" />
<Setter Property="Margin" Value="10"/>
<Setter Property="Width" Value="75" />
</Style>
</Grid.Resources>
<StackPanel Orientation="Vertical" Margin="20">
<TextBlock Text="{x:Bind Header}" FontSize="16" FontWeight="Bold" Margin="10">
</TextBlock>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Temp1" Style="{StaticResource styleTxtBlk}" />
<TextBox Style="{StaticResource styleTxtBox}" Text="{Binding MyDependencyProperty._List[0].Name}" />
</StackPanel>
</StackPanel>
</Grid>
UserControl Code
public AClass MyDependencyProperty
{
get { return (AClass)GetValue(MyDependencyPropertyProperty); }
set { SetValue(MyDependencyPropertyProperty, value); }
}
// Using a DependencyProperty as the backing store for MyDependencyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyDependencyPropertyProperty =
DependencyProperty.Register("MyDependencyProperty", typeof(AClass), typeof(TestUserControl), new PropertyMetadata(null));
public string Header = "TestHeader";
public TestUserControl()
{
this.InitializeComponent();
this.DataContext = this;
}
MainPage Xaml
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Foreground="Red" Text="{x:Bind ViewModel._AClass._List[1].Name}"/>
<local:TestUserControl Grid.Row="1" MyDependencyProperty="{x:Bind ViewModel._AClass}"/>
</Grid>
MainPage Code
public sealed partial class MainPage : Page
{
public TestViewModel ViewModel { get; set; }
public MainPage()
{
this.InitializeComponent();
ViewModel= new TestViewModel();
}
}
public class TestViewModel
{
public AClass _AClass { get; set; }
public TestViewModel()
{
_AClass = new AClass();
}
}
public class AClass
{
public List<BClass> _List { get; set; }
public AClass()
{
_List = new List<BClass>();
_List.Add(new BClass { Name = "123" });
_List.Add(new BClass { Name = "234" });
}
}
public class BClass
{
public string Name { get; set; }
}
And the result:

How can I set ComboboxItem isEnable property with binding?

I have a model view and a property called isEnable and I would like to set the comboboxItem IsEnable property with binding to property created in the model view , the problem is that I have datatemplate created inside a combobox , so it's not a simple Combobox , this is my code:
<ComboBox x:Name="ComboBoxUsers" ItemsSource="{Binding Users}" FontFamily="Arial" FontSize="11" Grid.Row="3" Height="30" Margin="10,5,5,5">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<Image />
<TextBlock />
</StackPanel>
<DataTemplate.Resources>
<Style TargetType="ComboBoxItem">
<Setter Property="IsEnable" Value="{Binding IsEnable}"/>
</Style>
</DataTemplate.Resources>
</DataTemplate>
</ComboBox.ItemTemplate>
Simple Class of Users:
public class Users
{
public string Name { get; set; }
public bool IsEnable { get; set; }
public Users()
{
}
}
How can I achieve that?
I solved my problem by adding this code :
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem"
<Setter Property="IsEnable" Value="{Binding IsEnable}" />
</Style>
</ComboBox.ItemContainerStyle>
So the complete code will be :
<ComboBox x:Name="ComboBoxUsers" ItemsSource="{Binding Users}" FontFamily="Arial" FontSize="11" Grid.Row="3" Height="30" Margin="10,5,5,5">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<Image />
<TextBlock />
</StackPanel>
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem" >
<Setter Property="IsEnable" Value="{Binding IsEnable}" />
</Style>
</ComboBox.ItemContainerStyle>
</DataTemplate>
</ComboBox.ItemTemplate>

MenuItem template change based on bound data

Basically I have a View which is bound to a ViewModel which has MenuItems.
What I want to do is that whenever the menu title is "-" I want to place a separator.
Theoretically it seems that I can avoid TemplateSelectors but if you think that it's inevitable please share even those solutions.
Here is the XAML:
<Window x:Class="WpfApp1.MenuItemStyle"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MenuItemStyle" Height="300" Width="300">
<DockPanel>
<Menu DockPanel.Dock="Top" ItemsSource="{Binding MenuItems}">
<Menu.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<HierarchicalDataTemplate.Triggers>
<DataTrigger Binding="{Binding Title}" Value="-">
<Setter Property="ContentTemplate">
<Setter.Value>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<Separator />
</HierarchicalDataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</HierarchicalDataTemplate.Triggers>
<TextBlock Text="{Binding Title}" Background="Red" />
</HierarchicalDataTemplate>
</Menu.ItemTemplate>
</Menu>
<Grid>
</Grid>
</DockPanel>
</Window>
And here is the code behind:
namespace WpfApp1
{
/// <summary>
/// Interaction logic for MenuItemStyle.xaml
/// </summary>
public partial class MenuItemStyle : Window
{
public MenuItemStyle()
{
InitializeComponent();
this.DataContext = this;
}
public ObservableCollection<MenuItem> MenuItems { get; set; } = new ObservableCollection<MenuItem> {
new MenuItem{ Title = "M1"
,Children= new ObservableCollection<MenuItem>{
new MenuItem{ Title = "M2"},
new MenuItem{ Title = "-"},
new MenuItem{ Title = "M3"},
}
}
};
}
public class MenuItem
{
public string Title { get; set; }
public ObservableCollection<MenuItem> Children { get; set; }
}
}
I have searched everywhere for a solution but couldn't find a pragmatic one.
You can create a Style for the MenuItems. Make it either local for a concrete menu instance (by placing in the menu's Resources) or place it in your resource dictionary:
<Menu ItemsSource="{Binding MenuItems}">
<Menu.Resources>
<Style TargetType="MenuItem">
<Style.Triggers>
<DataTrigger Binding="{Binding Title}" Value="-">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Separator/>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Menu.Resources>
<Menu.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Title}" Background="Red" />
</HierarchicalDataTemplate>
</Menu.ItemTemplate>
</Menu>

Stretch header width in grouped listbox view

How can i stretch the header width in a grouped listbox?
The listbox ItemSource is bound to hierarchical data structure. A group item contains a name and a list of subitems.
public class MyGroup
{
public string GroupName { get; set; }
public ObservableCollection<MyItem> SubItems { get; }
}
public class MyItem
{
public string ItemName { get; set; }
public DateTime Creation { get: set: }
}
public class MyViewModel:
{
public ObservableCollection<MyGroup> Data { get; set; }
}
The page is defined as follows
<Page>
<Page.Resources>
<CollectionViewSource x:Name="cvsListBoxGroup" IsSourceGrouped="True" Source="{Binding Data}" ItemsPath="SubItems"/>
</Page.Resources>
<Page.DataContext>
<local:MyViewModel/>
</Page.DataContext>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListBox Width="500" SelectionMode="Single" ItemsSource="{Binding Source={StaticResource cvsListBoxGroup}}">
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid Background="Beige">
<TextBlock Text="{Binding GroupName}"></TextBlock>
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListBox.GroupStyle>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Bisque">
<TextBlock Text="{Binding ItemName}" Foreground="Black" ></TextBlock>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Page>
Groups and items are displayed. The items don't fill the listbox full width so we need to set the HorizontalContentAlignment for ListBox.ItemContainerStyle. This trick is common - no problem
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListBox.ItemContainerStyle>
I tried to figure out some similiar trick for the groupheader items - but nothing helps.
<GroupStyle.HeaderContainerStyle>
<Style TargetType="GroupItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</GroupStyle.HeaderContainerStyle>
The problem seems the TargetType. For a listview there is a ListViewHeaderItem. But nothing similar exists for the listbox.
See the answer here, it's for ListView but that's basically the same:
https://social.msdn.microsoft.com/Forums/windowsapps/en-US/e8d4f7b3-93a8-4f17-9679-8b700b8d02e6/how-to-adjust-the-width-of-group-header-in-listview-to-stretch-full-width?forum=winappswithcsharp

Multiple DataTemplates for one ListBox

Imagine multiple object types in one Listbox. Both have different look and presented information. If I add any of them in ObservableCollection it displays them fine in ListBox.
<DataTemplate DataType="{x:Type local:DogData}" >
<Grid...
</DataTemplate>
<DataTemplate DataType="{x:Type local:CatData}" >
<Grid...
</DataTemplate>
Now I want users to be able to press a button and switch view to see more detailed information and there templates which provide it
<DataTemplate x:Key="TemplateDogDataWithImages" >
<Grid...
</DataTemplate>
<DataTemplate x:Key="TemplateCatDataWithImages" >
<Grid...
</DataTemplate>
but I can assign only one
AnimalsListBox.ItemTemplate = this.Resources["TemplateDogDataWithImages"] as DataTemplate;
I don't want to have one and have a bunch of triggers in it.
I have researched DataTemplateSelectors
http://tech.pro/tutorial/807/wpf-tutorial-how-to-use-a-datatemplateselector
Is there a better way? Is there a way to switch default templates for each data type so I could avoid DataTemplateSelectors?
On click of the button how do I select TemplateDogDataWithImages and TemplateCatDataWithImages as default ones and cliking other button use TemplateDogDataSimple and TemplateCatDataSimple?
I think a selector is easier (less code), but if you really want to use triggers, maybe something like this?
namespace WpfApplication65
{
public abstract class NotifyBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string property)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
public abstract class DataBase : NotifyBase
{
bool m_showDetailed;
public bool ShowDetailed
{
get { return m_showDetailed; }
set
{
m_showDetailed = value;
NotifyPropertyChanged("ShowDetailed");
}
}
}
public class DogData : DataBase { }
public class CatData : DataBase { }
public partial class MainWindow : Window
{
public List<DataBase> Items { get; private set; }
public MainWindow()
{
Items = new List<DataBase>() { new DogData(), new CatData(), new DogData() };
DataContext = this;
InitializeComponent();
}
}
}
<Window x:Class="WpfApplication65.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:WpfApplication65"
Title="MainWindow"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<DataTemplate DataType="{x:Type l:CatData}">
<DataTemplate.Resources>
<DataTemplate x:Key="basic">
<TextBlock Text="This is the basic cat view" />
</DataTemplate>
<DataTemplate x:Key="detailed">
<TextBlock Text="This is the detailed cat view" />
</DataTemplate>
</DataTemplate.Resources>
<Border BorderThickness="1"
BorderBrush="Red"
Margin="2"
Padding="2">
<StackPanel>
<ContentPresenter Name="PART_Presenter"
ContentTemplate="{StaticResource basic}" />
<CheckBox Content="Show Details"
IsChecked="{Binding ShowDetailed}" />
</StackPanel>
</Border>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding ShowDetailed}"
Value="True">
<Setter TargetName="PART_Presenter"
Property="ContentTemplate"
Value="{StaticResource detailed}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
<DataTemplate DataType="{x:Type l:DogData}">
<DataTemplate.Resources>
<DataTemplate x:Key="basic">
<TextBlock Text="This is the basic dog view" />
</DataTemplate>
<DataTemplate x:Key="detailed">
<TextBlock Text="This is the detailed dog view" />
</DataTemplate>
</DataTemplate.Resources>
<Border BorderThickness="1"
BorderBrush="Blue"
Margin="2"
Padding="2">
<StackPanel>
<ContentPresenter Name="PART_Presenter"
ContentTemplate="{StaticResource basic}" />
<CheckBox Content="Show Details"
IsChecked="{Binding ShowDetailed}" />
</StackPanel>
</Border>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding ShowDetailed}"
Value="True">
<Setter TargetName="PART_Presenter"
Property="ContentTemplate"
Value="{StaticResource detailed}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</Window.Resources>
<ListBox ItemsSource="{Binding Items}" />
</Window>

Categories

Resources