How to create a HierarchicalDataTemplate in code-behind? - c#

I have the need to create a HierarchicalDataTemplate for a TreeView in code-behind.
This is what my XAML looks like:
<DataTemplate x:Key="DetailTemplate">
<StackPanel Orientation="Horizontal">
<Image Height="15" Width="15" Source="{Binding Image}" Margin="0,0,5,0"/>
<TextBlock Text="{Binding Text}" />
</StackPanel>
</DataTemplate>
<HierarchicalDataTemplate x:Key="MasterDetailTemplate"
ItemsSource="{Binding SomeSource}"
ItemTemplate="{StaticResource DetailTemplate}">
<StackPanel Orientation="Horizontal">
<Image Height="15" Width="15" Source="{Binding Image}" Margin="0,0,5,0"/>
<TextBlock Text="{Binding Text}" />
</StackPanel>
</HierarchicalDataTemplate>
This is what i got so far in c#:
Image image = new Image();
image.Name = "image";
image.Height = 15;
image.Width = 15;
Binding imageBinding = new Binding("Image");
BindingOperations.SetBinding(image, Image.SourceProperty, imageBinding);
TextBlock textBlock = new TextBlock();
textBlock.Name = "textBlock";
Binding textBinding = new Binding("Text");
BindingOperations.SetBinding(textBlock, TextBlock.TextProperty, textBinding);
StackPanel stackPanel = new StackPanel();
stackPanel.Orientation = Orientation.Horizontal;
stackPanel.Children.Add(image);
stackPanel.Children.Add(textBlock);
DataTemplate dataTemplate = new DataTemplate();
dataTemplate.DataTemplateKey
I am stuck at the DataTemplateKey.
Is this possible to do in code behind?
Where do i go from here to set the x:Key value?

OK In my comment to your question I specified the code behind way of specifying templates. Now to use / refer them with a key when we add them into ResourceDictionaties, we must add them with a Key.
myWindow.Resources.Add("MasterDetailTemplate", dataTemplate);
Instead of myWindow it can be myParentPanel i.e. any ancestor of your tree view.
But there is one issue..
The Key (i.e. the DataTemplate) doesnt exist at design time. You are creating and adding it at runtime.
So if you are referring this datatemplate then
Either refer the resource after it is are added to the resource dictionary.
e.g.
myWindow.Resources.Add(
"MasterDetailTemplate",
dataTemplate);
myTreeView.ItemTemplate
= myWindow.Resources["MasterDetailTemplate"] as HierarchicalDataTemplate;
Refer the dynamically created data template as DynamicResource in XAML. DynamicResource removes the need of pre-existence of MasterDetailTemplate in any resource dictionary.
<TreeView ItemTemplate="{DynamicResource MasterDetailTemplate}" ... >
....
</TreeView>
Hope this helps.

I do that using resources in XAML:
<DataTemplate x:Key="TreeItemTemplate" DataType="{x:Type a:DriveStatusVar}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding PathName, NotifyOnSourceUpdated = True, NotifyOnTargetUpdated=True, Mode=TwoWay}" FontSize="10" Style="{StaticResource textBlockStyle}" IsEnabled="True"/>
</StackPanel>
</DataTemplate>
<HierarchicalDataTemplate x:Key="TreeModTemplate" DataType="{x:Type a:ModuleGroup}" ItemsSource="{Binding Items}">
<StackPanel Orientation="Horizontal">
<Image Source="{StaticResource add}" Width="15" Height="15"></Image>
<TextBlock Text="{Binding Name, NotifyOnSourceUpdated = True, NotifyOnTargetUpdated=True, Mode=TwoWay}" Style="{StaticResource textBlockStyle}" />
<TextBlock Text=" [" Foreground="Black" />
<TextBlock Text="{Binding Items.Count}" Foreground="Black" />
<TextBlock Text=" Items]" Foreground="Black" />
</StackPanel>
</HierarchicalDataTemplate>
and use them in code behind where define and create theTreeView object:
TreeView tree = new TreeView();
HierarchicalDataTemplate hdt = (HierarchicalDataTemplate)this.Resources["TreeModTemplat"];
hdt.ItemTemplate = (DataTemplate)this.Resources["TreeItemTemplate"];
tree.ItemTemplate = hdt;
//add itemsource
tree.ItemsSource = modList;
modList is a list of ModuleGroup class with a list Items.
Items is a list of DriveStatusVar class
internal class ModuleGroup : INotifyPropertyChanged, ICloneable
{
private bool _isSelected;
private bool _isExpanded;
private bool _isEdited;
private string _name;
private string _codesysName;
private int _codesysId;
private int _bits;
private int _level;
public ObservableCollection<DriveStatusVar> Items { get; set; }
public ObservableCollection<Alarm> Alarms { get; set; }
public List<string> States { get; set; }
public bool IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
OnPropertyChanged("IsSelected");
}
}
and so on... if anybody need more code, please tell me! this is just a piece of them.

Related

I want to put the image and text into the list box item(C# wpf)

The image of the list box item is too large.
I want to make this an item of constant height. Like the picture below.
This is my Code -
xaml:
<ListBox Grid.Row="0" x:Name="listBox" HorizontalAlignment="Stretch" Margin="0,0,0,0" VerticalAlignment="Stretch" AllowDrop="True" Drop="ListBox_Drop" DragEnter="ListBox_DragEnter" ScrollViewer.VerticalScrollBarVisibility="Visible" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<Image Margin="3" Source="{Binding Path}"/>
<TextBlock Margin="3" Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
cs:
class VideoListing
{
public string Name { get; set; }
public string Path { get; set; }
}
List<VideoListing> list = new List<VideoListing>();
public VideoPanel()
{
InitializeComponent();
list.Add(new VideoListing()
{
Name = "hello",
Path = #"C:\Users\johndoe\Desktop\Screenshot.png",
});
listBox.Items.Add(list);
}
You need to set either the Height or the MaxHeight properties on the Image control. Since you say you want a "constant height" then set the height of the image and tell it to scale the source appropriately.
<StackPanel Orientation="Vertical">
<Image Margin="3" Source="{Binding Path}" Height="64" Stretch="Uniform"/>
<TextBlock Margin="3" Text="{Binding Name}"/>
</StackPanel>
use the listView control in your application and use imagelist control to hold many images and set image index in a listview control.
I think it's too simple

WPF ItemsSource binding show only last item of list

How to show only last item of the list in itemssource binding ?
below is my current code.
xaml
<ItemsControl ItemsSource="{Binding UpgradeTicketStorage}" VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" Width="800">
<TextBlock Text="{Binding TicketTitle}" Style="{StaticResource TicketSelectionSubTitle}" TextAlignment="Left" />
<TextBlock Text="{Binding TicketDescription}" TextWrapping="Wrap" Style="{StaticResource TicketSelectionSubTitle2}" FontSize="19" TextAlignment="Left"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
from class binding i do have added 2 records which is ticketA and ticketB, how can i just display ticketB information ? instead of A and B
class
public class UpgradeTicketDescription : ViewModelBase
{
public string TicketTitle { get; set; }
public string TicketDescription { get; set; }
}
List<UpgradeTicketDescription> _UpgradeTicketStorage;
public List<UpgradeTicketDescription> UpgradeTicketStorage
{
get { return _UpgradeTicketStorage; }
set { _UpgradeTicketStorage = value; OnPropertyChanged("UpgradeTicketStorage"); }
}
UpgradeTicketStorage.Add(new UpgradeTicketDescription { TicketTitle = "TicketA", TicketDescription = "Observation DeckA (Single Ticket)"});
UpgradeTicketStorage.Add(new UpgradeTicketDescription { TicketTitle = "TicketB", TicketDescription = "Observation DeckB (Single Ticket)"});
If you want to bind to a specific item in a list you can go about it by creating a public variable you will bind to. Using what you have provided I've created an example that works for the last item in the list, all I did is create a new variable called LastItem and changed how the binding is in the project. This is just one of many ways of going about this.
xaml
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" Width="800">
<TextBlock Text="{Binding LastItem.TicketTitle}" TextAlignment="Left" />
<TextBlock Text="{Binding LastItem.TicketDescription}" TextWrapping="Wrap" FontSize="19" TextAlignment="Left"/>
</StackPanel>
class
public UpgradeTicketDescription LastItem
{
get { return UpgradeTicketStorage.Last(); }
}
This provides this output:

Data bind an ItemsControl in a DataTemplate

I seem to have a simple data-binding problem, but can't figure out the right way to do it. There is a TabControl which defines two DataTemplate's, one for the tab header and one for the tab content.
The content template contains an ItemsControl. The ItemsControl tries to bind to a dynamically created ViewModel (ConnectionInfoVM).
When I display the UI, the binding just fails, but there is no error-message in the output about it.
How do I have to set up the DataContext and the binding so the binding works and the DataBuffer is actually displayed? Any help greatly appreciated.
ConnectionsControl:
<UserControl x:Class="XXXViewer.Views.ConnectionsControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:viewModels="clr-namespace:XXXViewer.ViewModels"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TabControl Grid.Row="0" Name="TabDynamic" SelectionChanged="tabDynamic_SelectionChanged">
<TabControl.Resources>
<DataTemplate x:Key="TabHeader" DataType="TabItem">
<DockPanel>
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=Header}" />
<Button Name="btnDelete" DockPanel.Dock="Right" Margin="5,0,0,0" Padding="0" Click="btnTabDelete_Click" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=Name}">
<Image Source="{DynamicResource DeleteImg}" Height="11" Width="11"></Image>
</Button>
</DockPanel>
</DataTemplate>
<DataTemplate x:Key="TabContent" DataType="viewModels:ConnectionInfoVM">
<StackPanel>
<ScrollViewer Name="Scroller" Background="Black">
<StackPanel>
<TextBlock Text="This line gets printed" Foreground="White" FontFamily="Consolas"/>
<ItemsControl Name="ItemCtrl" ItemsSource="{Binding DataBuffer}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=.}" Foreground="White" FontFamily="Consolas"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
</StackPanel>
</DataTemplate>
</TabControl.Resources>
</TabControl>
</Grid>
</UserControl>
ConnectionsControl code behind:
namespace XXXViewer.Views
{
public partial class ConnectionsControl : UserControl
{
private readonly ObservableCollection<TabItem> _tabItems = new ObservableCollection<TabItem>();
public ConnectionsControl()
{
InitializeComponent();
// bindings
TabDynamic.ItemsSource = _tabItems;
TabDynamic.DataContext = this;
}
// assume this gets called
private void AddTabItem(ConnectionInfoVM ci)
{
DataTemplate headerTemplate = TabDynamic.FindResource("TabHeader") as DataTemplate;
DataTemplate contentTemplate = TabDynamic.FindResource("TabContent") as DataTemplate;
// create new tab item
TabItem tab = new TabItem
{
Header = $"Tab {ci.ConnectionID}",
Name = $"T{ci.ConnectionID}",
HeaderTemplate = headerTemplate,
ContentTemplate = contentTemplate,
DataContext = ci
};
_tabItems.Insert(0, tab);
// set the new tab as active tab
TabDynamic.SelectedItem = tab;
}
}
}
ConnectionInfoVM:
namespace XXXViewer.ViewModels
{
public class ConnectionInfoVM : ViewModelBase
{
private readonly ObservableQueue<string> _dataBuffer = new ObservableQueue<string>();
public ObservableQueue<string> DataBuffer => _dataBuffer;
}
}
Screenshot of the tab that gets created:
resulting tab
You set the ContentTemplate but never the Content, so the ContentTemplate is never applied because it's applied only when there's Content set. Instead of DataContext = ci write Content = ci.
By the way the DataContext = ci was useless because the DataContext is already implicitely the object on which the DataTemplate is applied.
Edit
As you're using WPF, use and abuse of its core feature: bindings.
How I would have written your code (if I didn't use full MVVM compliant code):
Your XAML:
<TabControl Grid.Row="0" Name="TabDynamic"
ItemsSource="{Binding TabItems, Mode=OneWay}"
SelectionChanged="tabDynamic_SelectionChanged">
<TabControl.Resources>
<DataTemplate x:Key="TabHeader" DataType="TabItem">
<DockPanel>
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=Header}" />
<Button Name="btnDelete" DockPanel.Dock="Right" Margin="5,0,0,0" Padding="0" Click="btnTabDelete_Click" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=Name}">
<Image Source="{DynamicResource DeleteImg}" Height="11" Width="11"></Image>
</Button>
</DockPanel>
</DataTemplate>
</TabControl.Resources>
<TabControl.ItemTemplate>
<DataTemplate DataType="viewModels:ConnectionInfoVM">
<TabItem Header="{Binding ConnectionID, Mode=OneWay}"
Name="{Binding ConnectionID, Mode=OneWay}"
HeaderTemplate="{StaticResources TabHeader}">
<StackPanel>
<ScrollViewer Name="Scroller" Background="Black">
<StackPanel>
<TextBlock Text="This line gets printed" Foreground="White" FontFamily="Consolas"/>
<ItemsControl Name="ItemCtrl" ItemsSource="{Binding DataBuffer}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=.}" Foreground="White" FontFamily="Consolas"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
</StackPanel>
</TabItem>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
You cs code become much simpler:
namespace XXXViewer.Views
{
public partial class ConnectionsControl : UserControl
{
private readonly ObservableCollection<ConnectionInfoVM> _tabItems = new ObservableCollection<ConnectionInfoVM>();
public ObservableCollection<ConnectionInfoVM> TabItems {get {return _tabItems;}}
public ConnectionsControl()
{
InitializeComponent();
// bindings
//TabDynamic.ItemsSource = _tabItems;
TabDynamic.DataContext = this;
}
// assume this gets called
private void AddTabItem(ConnectionInfoVM ci)
{
TabItems.Add(ci);
}
}
}
I noted while re-reading your code that you were probably confused about binding in code-behind.
Your code TabDynamic.ItemsSource = _tabItems; is not a binding, it will only set it once.
Anyway, I suggest you read a bit about MVVM. The TabItems should be in a ViewModel class instead of being in code-behind.
The Tabcontrol as per your coding does not contain in it's DataContext the viewmodel but the control; so we need to find a control or something else which holds the VM. It does not appear that the page holds the VM in its DataContext either.
I recommend that one route is to use the TabControl's Tag property to hold the VM such as specifying it in code behind as such:
TabDynamic.ItemsSource = _tabItems;
TabDynamic.DataContext = this;
TabDynamic.Tag = {Wherever you are keeping your VM at this time its not clear in your code example};
Then you can specify Tag from the template binding by specifying the TabControls' name as such:
<ItemsControl Name="ItemCtrl"
ItemsSource="{Binding Tag.DataBuffer,
ElementName=TabDynamic}">

WPF ComboBox with image

I'm trying to populate a Combo with images. It is defined as:
<ComboBox SelectedItem="{Binding SelectedLangComboItem}"
ItemsSource="{Binding Languages}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Image}" />
<TextBlock Text="{Binding Label}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Where the items are the LanguageItem classes:
public class LanguageItem
{
public System.Drawing.Bitmap Image { get; set; }
public string Label { get; set; }
public string Culture { get; set; }
public LanguageItem(System.Drawing.Bitmap image, string label, string culture)
{
Image = image;
Label = label;
Culture = culture;
}
}
Now, in my ViewModel c'tor I do:
_Languages = new ObservableCollection<LanguageItem>();
System.Reflection.Assembly app = System.Reflection.Assembly.GetExecutingAssembly();
System.IO.Stream file;
file = app.GetManifestResourceStream("MyNamespace.Images.FLAG1.gif");
_Languages.Add(new LanguageItem(new Bitmap(file), "ITALIAN", "it-IT"));
file = app.GetManifestResourceStream("MyNamespace.Images.FLAG2.gif");
_Languages.Add(new LanguageItem(new Bitmap(file), "ENGLISH", "en-EN"));
this.SelectedLangItem = _Languages[0];
The images are embedded resources. Here I have two problems:
The images are not displayed;
The Item is not selected, the SelectedLangItem is:
public LanguageItem SelectedLangItem
{
get { return _SelectedLangItem; }
set
{
if (_SelectedLangItem == value)
return;
_SelectedLangItem = value;
this.RaisePropertyChanged("SelectedLangItem");
}
}
Use
new BitmapImage(new Uri("MyNamespace.Images.FLAG1.gif", UriKind.Relative));
as it have to implement ImageSource
And regarding not selected: Property name is "SelectedLangItem" while in xaml SelectedLangComboItem if you did not mistype.
CODE:
this.RaisePropertyChanged("SelectedLangItem");
XAML:
<ComboBox SelectedItem="{Binding SelectedLangComboItem}"
Your problem is that you are trying to bind an Image to the Image.Source property, which is of type ImageSource.
The easiest solution is to add your actual image files into a folder and change the Image property in your class to a string that holds the file path to the image in this format:
/ApplicationName;component/ImageFolderName/ImageName.png
Then you can correctly bind this string (which the Framework will convert into an ImageSource object) to the Image.Source property in your DataTemplate.
Try below xaml code and bind image list into combobox...
<Window.Resources>
<DataTemplate x:Key="cmbTemplate">
<WrapPanel Margin="0 5 0 5" Height="80">
<Image Width="65" Height="65" Stretch="Fill" Source="{Binding Photo}" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="0,0,15,0"/>
<Label Content="{Binding Name}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="20"/>
</WrapPanel>
</DataTemplate>
</Window.Resources>
<StackPanel HorizontalAlignment="Center">
<Label Content="Dropdown list with Image" HorizontalAlignment="Center" FontSize="30" Margin="20"/>
<ComboBox x:Name="lstwithimg" HorizontalAlignment="Center" VerticalAlignment="Top" ItemTemplate="{StaticResource cmbTemplate}" Height="80" Width="400"/>
</StackPanel>
Check below... for more understanding of live example...
http://www.codescratcher.com/wpf/wpf-combobox-with-image/

Binding controls inside ListBox to a source other than the source of ListBox

I have a ListBox bound to a source which provides data to the text property of the controls inside. Now I'd like to bind Foreground property of my textboxes to a different source other than the one the main ListBox is bound to!
My listbox is bound to a ObservableCollection and I want my textblock Foreground property bound to textColor which is located in ViewModel
public SolidColorBrush textColor
{
get { return new SolidColorBrush(Colors.Red); }
}
both are in ViewModel class.
I tried using Foreground="{Binding textColor}" but it seems XAML doesn't see it at all, should I do anything in the page so that it can see it, or is it because the parent (ListBox) is using a different source?
Edit :
More details:
I have a DataContext.cs class which I have defined my tables in it.
I have a ViewModel.cs class which I have these in it
public class CViewModel : INotifyPropertyChanged
{
private CDataContext myDB;
public CViewModel(string DBConnectionString)
{
myDB = new CDataContext(DBConnectionString);
}
private ObservableCollection<Groups> _allGroups;
public ObservableCollection<Groups> AllGroups
{
get { return _allGroups; }
set
{
_allGroups = value;
NotifyPropertyChanged("AllGroups");
}
}
public string textColor
{
get { return "Tomato"; }
}
}
Then I have my XAML file MainPage.xaml:
....
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ListBox Margin="0,8,0,0" toolkit:TiltEffect.IsTiltEnabled="True" x:Name="list" ItemsSource="{Binding AllGroups}" HorizontalAlignment="Center" BorderThickness="4">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Orange" Width="125" Height="125" Margin="6" Tap="Counterlist_OnTap">
<TextBlock Name="gname" Foreground="White" Text="{Binding Name}" VerticalAlignment="Center" HorizontalAlignment="Center" TextAlignment="Center" TextWrapping="Wrap"/>
<TextBlock Name="ccl" Margin="0,0,0,-5" Foreground="{Binding textColor}" Text="{Binding Count}" FontSize="26" VerticalAlignment="Bottom" HorizontalAlignment="Left" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
....
I also set DataContext of my MainPage to ViewModel in code behind:
this.DataContext = App.ViewModel;
The textColor property is part of the CViewModel, not the Groups object that is the datacontext within the ItemTemplate.
Within the ItemTemplate you can reach out to the parent ViewModel with the following Element binding:
<TextBlock Name="ccl" Margin="0,0,0,-5"
Foreground="{Binding ElementName=ContentPanel, Path=DataContext.textColor}"
Text="{Binding Count}" FontSize="26"
VerticalAlignment="Bottom" HorizontalAlignment="Left" />
All you have to do is declare a static class (e.g. a singleton with per-instance access) and explicitly set the property binding to look in that class instead of the parent-bound model.
Bottom line: Just set the Source explicitly through a StaticResource.

Categories

Resources