Selection & identification of child node in TreeView - c#

I have created a treeview in my xaml.
<TreeView Name="exportTreeView" ItemsSource="{Binding}" Width="350" >
<TreeView.Resources>
<DataTemplate x:Key="layersTemplate">
<StackPanel Orientation="Horizontal" Margin="10,0,0,0">
<CheckBox Foreground="White" IsChecked="{Binding IsToBeExported}" VerticalAlignment="Center" />
<Label Style="{StaticResource baseStyle}" Content="{Binding Path=Name}" VerticalAlignment="Center" />
</StackPanel>
</DataTemplate>
<HierarchicalDataTemplate x:Key="objectTemplate" ItemsSource="{Binding Path=LayersList}" ItemTemplate="{StaticResource ResourceKey=layersTemplate}">
<StackPanel Orientation="Horizontal" Height="15" Margin="10,0,0,0">
<CheckBox Foreground="White" IsChecked="{Binding IsToBeExported}" VerticalAlignment="Center" />
<Label Style="{StaticResource baseStyle}" Content="{Binding Path=Name}" VerticalAlignment="Center" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=ObjectList}" ItemTemplate="{StaticResource ResourceKey=objectTemplate}">
<StackPanel Orientation="Horizontal" Margin="10,0,0,0">
<CheckBox Foreground="White" IsChecked="{Binding IsToBeExported}" VerticalAlignment="Center" />
<Label Style="{StaticResource baseStyle}" Content="{Binding Path=Name}" VerticalAlignment="Center" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
The tree structure is like below. Each Parent can have any number of children & each Child can have any number of Grandchildren. Multiple selection is allowed too.
Parent
-Child
--Grandchild
I have checkboxes for all levels. I am not getting how to access its nodes individually and also how to use the tree data.
In my VM class, I set the datacontext of this TreeView to a 3 class list like below:
public class MProject
{
public string Name { get; set; }
public bool IsToBeExported { get; set; }
public List<MWorkObject> ObjectList { get; set; }
}
public class MWorkObject
{
public string Name { get; set; }
public bool IsToBeExported { get; set; }
public List<MLayer> LayersList { get; set; }
}
public class MLayer
{
public string Name { get; set; }
public bool IsToBeExported { get; set; }
}
My requirement is:
Selecting the parent should select all its child and grandchild.
How to identify in the code which item is selected ? Need it to do further processing.
Please help.

You need to implement INotifyPropertyChanged for your classes. Then
in setter of IsToBeExported in MProject handle all children (set
IsToBeExported to what you need). The binding makes the change
visible in tree
if IsToBeExported set to true, then it is selected
Example:
public class ViewBase :INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string info)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(info));
}
}
public class MProject : ViewBase
{
public string Name
{
get
{
return _name;
}
set
{
if (value != _name)
{
_name = value;
NotifyPropertyChanged(nameof(Name));
}
}
}
private string _name;
...
}

Related

How to fix ListView Data Binding to display added items?

I am trying to do ListView with ItemTemplate with CheckBox and TextBlock control and bind data to it. However, the list does not display added items.
Here is my XAML code:
<ListView x:Name="WarrantyCategory_ListView" HorizontalAlignment="Stretch" Grid.Row="3" VerticalAlignment="Center" SelectionMode="Single" Header="Category:" FontSize="16" Foreground="#FFC7C7C7" Margin="10,0" ItemsSource="{Binding WarrantyCategories}" >
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<CheckBox x:Name="Description_CheckBox" IsChecked="{Binding IsChecked, Mode=TwoWay}"/>
<TextBlock x:Name="DescriptionItem_TextBlock" Text="{Binding Category, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Here is my WarrantyCategory class:
public class WarrantyCategory
{
public bool IsChecked { get; set; }
public string Category { get; set; }
public WarrantyCategory(bool isChecked, string category)
{
IsChecked = isChecked;
Category = category;
}
}
and my Page code behind:
public AddWarrantyDescriptionPage()
{
this.InitializeComponent();
DisplayInformation.AutoRotationPreferences = DisplayOrientations.Portrait;
InitializeCategoryList();
}
private void InitializeCategoryList()
{
WarrantyCategories.Add(new WarrantyCategory(false, "Computers"));
WarrantyCategories.Add(new WarrantyCategory(false, "Mobile"));
WarrantyCategories.Add(new WarrantyCategory(false, "Music"));
WarrantyCategories.Add(new WarrantyCategory(false, "Video"));
}

DataContext Gridview within Stack Panel in XAML Windows Phone 8

I'm trying to data bind a collection ("Dashboard") that contains a ObservableCollection property.
I have managed to databind the dashboard class without any issues. I cant however work out how to databind to the Release collection that is contained within the dashboard class.
The issue seems to be on the GridView which is databound to the Releases property of the Dashboard class. The Stack Panel around the GridView is working correctly.
The classes
public class Dashboard
{
public Dashboard(String id, String projectName)
{
this.Id = id;
this.ProjectName = projectName;
this.Releases = new ObservableCollection<Release>();
}
public string Id { get; private set; }
public string ProjectName { get; private set; }
public ObservableCollection<Release> Releases { get; private set; }
public override string ToString()
{
return this.ProjectName;
}
}
public class Release
{
public Release(string environmentName, string releaseVersion, string state, string releaseDate)
{
EnvironmentName = environmentName;
ReleaseVersion = releaseVersion;
State = state;
ReleaseDate = releaseDate;
}
public string EnvironmentName { get; private set; }
public string ReleaseVersion { get; private set; }
public string State { get; private set; }
public string ReleaseDate { get; private set; }
}
The XAML
<HubSection x:Uid="Dashboard" x:Name="Dashboard" Header="Dashboard" DataContext="{Binding Dashboard}">
<DataTemplate>
<GridView x:Uid="DashboardGrid" x:Name="DashboardGrid" ItemsSource="{Binding}" ItemTemplate="{StaticResource Standard200x180TileItemTemplate}" >
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
</DataTemplate>
</HubSection>
The data template
<DataTemplate x:Key="Standard200x180TileItemTemplate">
<StackPanel DataContext="{Binding}" >
<TextBlock Text="{Binding ProjectName}" Grid.Column="0" Style="{ThemeResource BaseTextBlockStyle}" Typography.Capitals="SmallCaps" Grid.Row="0" IsTextScaleFactorEnabled="False"/>
<GridView Grid.Row="1" DataContext="{Binding Releases}">
<TextBlock Text="{Binding EnvironmentName}" />
<TextBlock Text="{Binding ReleaseVersion}" />
<TextBlock>hello</TextBlock>
<Border Background="#FF0CB90C" Height="110" Width="110" HorizontalAlignment="Left" Margin="0,0,10,0">
</Border>
</GridView>
</StackPanel>
</DataTemplate>
Set ItemsSource of GridView not DataContext and then use another DataTemplate
<StackPanel>
<TextBlock Text="{Binding ProjectName}" Grid.Column="0" Style="{ThemeResource BaseTextBlockStyle}" Typography.Capitals="SmallCaps" Grid.Row="0" IsTextScaleFactorEnabled="False"/>
<GridView Grid.Row="1" ItemsSource="{Binding Releases}">
<GridView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding EnvironmentName}" />
<TextBlock Text="{Binding ReleaseVersion}" />
<TextBlock>hello</TextBlock>
<Border Background="#FF0CB90C" Height="110" Width="110" HorizontalAlignment="Left" Margin="0,0,10,0">
</Border>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</StackPanel>

Retrieving bound object on node from TreeView

I'm not an expert in WPF so forgive me if I am wording my question weirdly. I'd be more than happy to elaborate if anything doesn't make sense.
I have a treeview that binds an observablecollection of a class. When my program launches, I read every C sourcecode files at a particular destination, store its name and filepath in the class mentioned.
Here is my XAML:
<TreeView Name="ProgramTree" ItemsSource="{Binding ProgramItemCollection}"
cal:Message.Attach="[Event PreviewMouseRightButtonDown] = [Action TestRight($dataContext,$eventArgs)];
[Event PreviewMouseDoubleClick] = [Action NodeDoubleClick($dataContext,$eventArgs)]">
<TreeView.Resources>
<!--DataTemplate for Program Nodes (Top) and binds FileItemNodes-->
<HierarchicalDataTemplate DataType="{x:Type my:ProgramItem}"
ItemsSource="{Binding FileItemCollection}">
<Border Width="100" BorderBrush="RoyalBlue"
Background="RoyalBlue" BorderThickness="1"
CornerRadius="2" Margin="2" Padding="2" >
<StackPanel Orientation="Horizontal">
<Image Style="{StaticResource IconStyle}" Margin="2" Source="{StaticResource FolderIcon}" />
<TextBlock Margin="2" Text="{Binding ProgramName}"
Foreground="White" FontWeight="Bold"/>
</StackPanel>
</Border>
</HierarchicalDataTemplate>
<!--DataTemplate for File Nodes (Subnodes of Program Nodes)-->
<HierarchicalDataTemplate DataType="{x:Type my:FileItem}">
<Border Width="80" Background="LightBlue" CornerRadius="2" Margin="1" >
<StackPanel Orientation="Horizontal">
<Image Margin="2" />
<TextBlock Margin="2" Text="{Binding NodeName}" />
</StackPanel>
</Border>
</HierarchicalDataTemplate>
</TreeView.Resources>
Codebehind:
public class FileItem
{
public string NodeName { get; set; }
public string FullName { get; set; }
public string Extension { get; set; }
}
public class ProgramItem : PropertyChangedBase
{
private ObservableCollection<FileItem> fileItemCollection;
...
What I now want to do is hook a double click event on the node and open the relevant file.
public void NodeDoubleClick(object sender, MouseButtonEventArgs e)
{
TreeViewItem treeViewItem = VisualUpwardSearch(e.OriginalSource as DependencyObject);
if (treeViewItem != null)
{
//Open file
}
}
private static TreeViewItem VisualUpwardSearch(DependencyObject source)
{
while (source != null && !(source is TreeViewItem))
source = VisualTreeHelper.GetParent(source);
return source as TreeViewItem;
}
I can retrieve the double clicked node (treeviewitem) without a problem. The problem is I want to retrieve an object of FileItem from the node I double clicked to access the filepath property. Is this possible at all?
It is possible by resolving the DataContext of the TreeViewItem:
FileItem fileItem = (treeViewItem.DataContext as FileItem);
A more elegant way would be to use MouseInput Bindings and a Command in your FileItem class.
In your Datatemplate for FileItem:
<StackPanel Orientation="Horizontal">
<StackPanel.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick"
Command="{Binding OpenFileCommand}" />
</StackPanel.InputBindings>
<Image Margin="2" />
<TextBlock Margin="2" Text="{Binding NodeName}" />
</StackPanel>
In your FileItem:
public class FileItem
{
public FileItem()
{
this.OpenFileCommand
= new SimpleCommand(()=> Process.StartNew(this.FullName));
}
public string NodeName { get; set; }
public string FullName { get; set; }
public string Extension { get; set; }
public ICommand OpenFileCommand { get; set;}
}
P.S.: If you are not used to WPF's Commands, a basic implementation of a simple ICommand could be:
public class SimpleCommand : System.Windows.Input.ICommand
{
public SimpleCommand(Action action)
{
this.Action = action;
}
public Action Action { get; set; }
public bool CanExecute(object parameter)
{
return (this.Action != null);
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
if (this.Action != null)
{
this.Action();
}
}
}
Commands are much more effective for such szenarios. You dont need to walk the visual Tree and you do not need code behind at all.
Check the DataContext property of TreeViewItem and try to cast it to FileItem type.
Also you can define the template for FileItem as simple DataTemplate, not the HierarchicalDataTemplate.

Binding Variable in List to Textblock INotifyPropertyChanged

I want to Bind a String element in a List to a Textbox. When i click an element the Text gets updated. But when the element in the List changes the Textbox Text doesn't change..
My Code looks like this:
XAML:
<TextBlock Foreground="White" Name="xTitel" Text="{Binding Titel}" Width="200" FontSize="36" FontWeight="Bold"/>
OnClick Function:
foreach (Channel c in App.Connector.ChannelList)
{
if (c.StationName == StationName)
{
Binding b = new Binding();
b.Source = c.CurrentTitle;
xTitel.SetBinding(TextBlock.TextProperty, b);
}
}
Channel Definition:
public class GlobalVariables : INotifyPropertyChanged
{
public static MediaElement mediaElement;
private ObservableCollection<Channel> channelList;
public ObservableCollection<Channel> ChannelList
{
get { return channelList; }
set
{
channelList = value;
NotifyPropertyChanged("ChannelList");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
EDIT:
What i forgot to say:
In my XAML i have a GridView where it works with Channellist Binding. If I am correct the Textbox should update everytime when my Gridview Updates. This is my Gridview:
<local:VariableGridView
IsSwipeEnabled="True"
Background="Transparent"
x:Name="itemGridView"
Margin="0,0,0,-3"
Padding="116,0,40,46"
SelectionMode="None"
IsItemClickEnabled="True"
ItemClick="ItemView_ItemClick"
Grid.RowSpan="2"
>
<local:VariableGridView.ItemsPanel>
<ItemsPanelTemplate>
<VariableSizedWrapGrid Background="Transparent" Orientation="Vertical" ItemWidth="120" ItemHeight="120"/>
</ItemsPanelTemplate>
</local:VariableGridView.ItemsPanel>
<local:VariableGridView.ItemTemplate>
<DataTemplate>
<Grid Background="{Binding Background}">
<StackPanel Orientation="Horizontal" Margin="10,0,0,0">
<Image x:Name="CurrentCover" Source="{Binding CurrentCover}" Width="90" Height="90"/>
<StackPanel Orientation="Vertical" HorizontalAlignment="Left" Margin="10,25,10,10">
<TextBlock Foreground="White" Text="{Binding Name}" Width="200" FontSize="24" FontWeight="Bold"/>
<TextBlock Foreground="White" Text="{Binding CurrentArtist}" Width="200" FontSize="12" FontWeight="Bold" />
<TextBlock Foreground="White" Text="{Binding CurrentTitle}" Width="200" FontSize="12" />
</StackPanel>
</StackPanel>
</Grid>
</DataTemplate>
</local:VariableGridView.ItemTemplate>
</local:VariableGridView>
And the Channel class:
public class Channel : INotifyPropertyChanged
{
public int Width { get; set; }
public int Height { get; set; }
public string Logo { get; set; }
public string StationName { get; set; }
public string Color { get; set; }
public SolidColorBrush Background { get; set; }
//public string Name { get; set; }
private string name;
public string Name
{
get { return name; }
set
{
name = value;
NotifyPropertyChanged("Name");
}
}
//..... More Code ...
}
(The Textboxes in this Gridview do Update. But the other textboxes, where I want to set the Binding over the Code don't update)
Should it be this?
Binding b = new Binding("CurrentTitle");
b.Source = c;
xTitel.SetBinding(TextBlock.TextProperty, b);
Edit to explain a little more: If you bind directly to the string property you will not see the changes because strings are immutable. It'll be set on the original string forever. You have to bind to the object that holds the string property and give the binding a property name (path) to watch for changes to.

TreeViewItem expaning like a chain-event

In my WPF application, if I open one of the treeviewitems to display all the child items, it goes on a chain event and slowly starts opening all the parent items. For example if I opened the Red, it'd slowly open the blue's until they are all open;
My TreeView Code;
<TreeView x:Name="tvTagList" Margin="15, 40, 15, 50" SelectedItemChanged="tvTagList_SelectedTagChanged" ItemsSource="{Binding}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Background="{x:Null}" BorderBrush="{DynamicResource ExtryzeAccentBrushSecondary}" BorderThickness="2" ScrollViewer.CanContentScroll="True" Foreground="White"
VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type DataBind:TagClass}" ItemsSource="{Binding Children}" >
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
<TextBlock Text="{Binding TagClassMagic}" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type DataBind:TagEntry}">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
<TextBlock Text="{Binding TagFileName}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
My classes;
public class TagClass
{
public string TagClassMagic { get; set; }
public ITagClass RawClass { get; set; }
public List<TagEntry> TagEntries = new List<TagEntry>();
public IList Children
{
get
{
return new CompositeCollection()
{
new CollectionContainer() { Collection = TagEntries }
};
}
}
}
public class TagEntry
{
public string TagFileName { get; set; }
public ITagEntry RawTag { get; set; }
}
I think this is because Children every time returns a new CompositeCollection which is propably expanded by default. Try returning TagEntries directly or return a new CompositeCollection at first time only.
Turns out we had to change;
VirtualizingStackPanel.VirtualizationMode="Recycling"
to
VirtualizingStackPanel.VirtualizationMode="Standard"

Categories

Resources