How to set different HorizontalAlignment to ListBoxItems - c#

I posted a question yesterday but I think I failed to explain it correctly.
Let me try again.
So this is my goal:
The red speech bubble represents an incoming message, and the blue bubble an outgoing message. I can describe this more precisely with the following xaml code. Note that the following code is only an explanation of what I expect to get when my actual xaml code (with some DataTemplates) compiles (WPF will populates the data automatically for me, using the DataTemplates). :
<ListBox>
<ListBoxItem HorizontalAlignment="Right">
<Grid Background="Blue">
<TextBlock Text="Help me please!" FontSize="30"/>
</Grid>
</ListBoxItem>
<ListBoxItem HorizontalAlignment="Left">
<Grid Background="Red">
<TextBlock Text="What do you want?" FontSize="30"/>
</Grid>
</ListBoxItem>
<ListBoxItem HorizontalAlignment="Right">
<Grid Background="Blue">
<TextBlock Text="I want a ListBox" FontSize="30"/>
</Grid>
</ListBoxItem>
<ListBoxItem HorizontalAlignment="Left">
<Grid Background="Red">
<TextBlock Text="Then?" FontSize="30"/>
</Grid>
</ListBoxItem>
<ListBoxItem HorizontalAlignment="Right">
<Grid Background="Blue">
<TextBlock Text="But the Grid won't fill" FontSize="30"/>
</Grid>
</ListBoxItem>
</ListBox>
In order to achive this, I wrote:
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<ListBoxItem>
<Grid Background="{Binding Color}">
<TextBlock Text="{Binding Text}" FontSize="30"/>
</Grid>
</ListBoxItem>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Note that alignment is not specified in the code above, becuase I really don't know how to set different alignement for ListBoxItem separately using templates. So this would result in the situation where all the blue and red grids are all aligned to the left, by default.
My first approach includes a Data Template selector (The template for incoming messages is omitted):
<ListBox>
<ListBox>
<ListBox.ItemTemplate>
<!-- local:MessageBubbleTemplateSelector.OutgoingMessageTemplate -->
<DataTemplate>
<ListBoxItem>
<Grid>
<Grid Background="{Binding Color}" HorizontalAlignment="Right">
<TextBlock Text="{Binding Text}" FontSize="30"/>
</Grid>
</Grid>
</ListBoxItem>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</ListBox>
But this did not work. Because the Grid which wraps the speech bubble won't expand automatically, so the alignment of the Grid inside this Grid did not matter (tightly fitted).
And then I went for searching how to expand a Grid inside a StackPanel, and got no luck.
After many hours of googling and trials and errors, I decided to define the template for the ItemsPanelTemplate myself. I have a property in my Message object that can help me tell an incoming message from an outgoing one. But I don't know how to create an ItemsPanelTemplate selector (For the record, Google told me that Style.Trigger is not supported in Windows Phone 8).
So my question is: how to set different HorizontalAlignment for ListBoxItems?
BTW, ItemsPabelTemplate looks like this:
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
Thank you so much for your patience. I am madly desperate here already... So many hours wasted on this...

Note: I do not have Phone SDK so had to make do with normal WPF app. I have not used triggers as you mentioned they do not work.
So I knocked up a simple app that looks like this
Here's the code:
App.xaml.cs
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var mainvm = new MainWindowViewModel();
var window = new MainWindow
{
DataContext = mainvm
};
window.Show();
mainvm.Messages.Add(new OutgoingMessage{ MessageContent = "Help me please!"});
mainvm.Messages.Add(new IncomingMessage { MessageContent = "What do you want" });
mainvm.Messages.Add(new OutgoingMessage { MessageContent = "I want a ListBox" });
mainvm.Messages.Add(new IncomingMessage { MessageContent = "Then?" });
mainvm.Messages.Add(new OutgoingMessage { MessageContent = "But the Grid won't fill" });
}
}
MainWindow.xaml
<Window x:Class="ChatUI.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ChatUI"
Title="MainWindow" Height="350" Width="200">
<Window.Resources>
<DataTemplate DataType="{x:Type local:IncomingMessage}">
<Grid Margin="0,10">
<Border CornerRadius="8" Background="Red" BorderBrush="Black" BorderThickness="1" />
<TextBlock Text="{Binding MessageContent}" HorizontalAlignment="Left" Margin="5" Foreground="White"/>
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type local:OutgoingMessage}">
<Grid Margin="0,10">
<Border CornerRadius="8" Background="Blue" BorderBrush="Black" BorderThickness="1" />
<TextBlock Text="{Binding MessageContent}" HorizontalAlignment="Right" Margin="5" Foreground="White"/>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid Background="Black">
<ItemsControl ItemsSource="{Binding Path=Messages}"/>
</Grid>
ViewModelBase.cs
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, e);
}
}
}
MainWindowViewModel:
public class MainWindowViewModel : ViewModelBase
{
public MainWindowViewModel()
{
Messages = new ObservableCollection<Message>();
}
public ObservableCollection<Message> Messages { get; protected set; }
}
Message.cs:
public abstract class Message : ViewModelBase
{
private string _messageContent;
public string MessageContent
{
get
{
return this._messageContent;
}
set
{
this._messageContent = value;
this.OnPropertyChanged("MessageContent");
}
}
}
OutgoingMessage.cs
public class OutgoingMessage : Message
{
}
IncomingMessage.cs
public class IncomingMessage : Message
{
}
How this works
I override the application startup so I can create viewmodels to populate my UI. You can see in the App.xaml.cs code I create the Window and show it, and then add the messages. I was going to use a timer but got lazy.
If you look at the MainWindow.xaml, you will notice that I have 2 DataTemplates defined. One of them targets my IncomingMessageViewModel and the other targets the OutogingMessageViewModel. The local prefix is an alias for my application namespace. I have an ItemsControl that can contain the base type Message class, just so that I can have both Incoming and Outgoing messages in the same collection. This is bound to the Messages property on my MainWindowViewModel class. It is important to have incoming and outgoing messages as 2 separate classes as this is the magic that makes this work.
An alternative technique would be to use a property with a style selector bound to the property as one of the other answers suggest, but this would mean that I would have to deal with UI specific logic in my ViewModel (which I don't like to do).
To change the appearance of either Message type, just change the xaml code in the respective DataTemplate.
Hope this helps.

In WPF you would need to add
<ListBox.ItemContainerStyle>
<Style>
<Setter Property="HorizontalAlignment" Value="{Binding WHATEVER}" />
</Style>
</ListBox.ItemContainerStyle>
to your ListBox and set the "WHATEVER" to a property of your items that has the alignment specified... I don't know if that works for Windows Phone but it seems worth a try since you didn't mention the ItemContainerStyle...

Instead of using Grid, use DockPanel with HorizontalAlignment="Stretch" instead.
As for the data alignment, assuming you are using ItemsSource, then there is some workaround.
First, the easy thing to do is to add HorizontalAlignment WPF property to your message class. The message class will determine whether the HorizontalAlignment will be left or right. However this will make dependency more higher with the UI.
The code will be like this:
<TextBlock Text="{Binding Text}" HorizontalAlignment="{Binding MessageHAlign}" />
Second, the better (or clean) way to do is to do HorizontalAlignment binding with converter (IValueConverter). It is harder and you must define your own converter, but your code will be tidier. Then your message has an enum of Income or Outcome message, named MessageType. Then in your converter define it like:
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
if(parameter is MessageType){
if(((MessageType)parameter) == MessageType.Income){
return HorizontalAlignment.Left;
}
else{
return HorizontalAlignment.Right;
}
}
}
The code above is not tested, so please consider error.
For the implementation of Converter, please search it in some places. I still cannot generate Converter binding without help source :)

Try this and set Property according to incoming as Left and Outgoing as Right
<DataTemplate>
<ListBoxItem>
<Grid>
<Grid Background="{Binding Color}" HorizontalAlignment="{Binding Property}">
<TextBlock Text="{Binding Text}" FontSize="30"/>
</Grid>
</Grid>
</ListBoxItem>
</DataTemplate>

that would be my dirty working example
code-behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var l = new List<lItem>();
for(int i=0;i<5;i++)
{
l.Add(new lItem(true,"aaa"+i));
l.Add(new lItem(false,"bbb"+i));
}
sads.ItemsSource = l;
}
}
public class lItem
{
public string Text { get; set; }
public Brush Color { get; set; }
public HorizontalAlignment alig { get; set; }
public lItem(bool ss, string str)
{
Text = str;
Color = Brushes.Blue;
alig = HorizontalAlignment.Right;
if (ss)
{
Color = Brushes.Red;
alig = HorizontalAlignment.Left;
}
}
}
Xaml
<ListBox Name="sads" Width="230">
<ListBox.ItemTemplate>
<DataTemplate>
<ListBoxItem>
<Grid Width="200">
<Label Background="{Binding Color}" VerticalAlignment="Top" HorizontalAlignment="{Binding alig}" >
<TextBlock Text="{Binding Text}" FontSize="30"/>
</Label>
</Grid>
</ListBoxItem>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
i would recommend to use trigger instead of define visual parts in your ViewModel

Related

How to add a ListViewItem which contains a Grid, StackPanel and a TextBlock

I have a ListView. I need to add ListViewItems programatically that contain a Textblock nested inside of a StackPanel, nested inside of a Grid (For the purpose of formatting the text). I am relatively new to WPF and I cannot find an answer. Here is the code that I would like each ListViewItem to have once added:
<ListViewItem Padding="15">
<Grid Width="1285">
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal" Width="Auto">
<TextBlock Text="ITEM" VerticalAlignment="Center" />
</StackPanel>
</Grid>
</ListViewItem>
Here is an image to demonstrate what I am trying to do.The code above puts the ListViewItem in the middle, but by using a Grid and a StackPanel, I was able to center the text (StackPanel was actually for the purpose of adding an icon alongside it but I've temporarily taken that out. If someone knows how to do this better then by all means tell me.
So, what you need is a UserControl, which will be used to display each item in your ListView. So you must design your user control the way you want it to look; so if you need a TextBlock inside a panel inside a grid, that's what you must do.
<UserControl x:Class="SOWPF.MyListViewItem"
....
mc:Ignorable="d"
d:DesignHeight="48" d:DesignWidth="250">
<Grid>
<StackPanel Orientation="Horizontal" Width="250" Height="36" Margin="10" Background="PeachPuff">
<TextBlock Background="White" Width="200" Height="32" Margin="2" Text="{Binding DisplayText}"/>
</StackPanel>
</Grid>
</UserControl>
To display data, you must have a class with public properties. So I have this simple class with one public string property, which will contain the text you want to display in the TextBlock. The data binding on the user control refers to this; DisplayText is the public string property:
public class DisplayData
{
public string DisplayText { get; set; }
}
Now in your View, you must use a ContentControl inside your ListView to display your UserControl dynamically.
<Window x:Class="SOWPF.MainWindow"
....
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<ListView>
<ItemsControl ItemsSource="{Binding DisplayList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:MyListViewItem/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ListView>
</Grid>
</Window>
And here's your code behind. I did it this way for convenience, but you really should use a ViewModel.
public partial class MainWindow : Window
{
public List<DisplayData> DisplayList { get; set; }
public MainWindow()
{
InitializeComponent();
DisplayList = new List<DisplayData>
{
new DisplayData() { DisplayText = "A" },
new DisplayData() { DisplayText = "B" },
new DisplayData() { DisplayText = "C" }
};
DataContext = this;
}
}
Result:
EDIT (After OP edited the question)
If all you want to do is center text, you can get rid of extra controls and simply use TextAlignment=Center in your TextBlock.
<UserControl x:Class="SOWPF.MyListViewItem"
....
mc:Ignorable="d"
d:DesignHeight="48" d:DesignWidth="250">
<TextBlock Background="LightCoral" Width="200" Height="32" Margin="2" Text="{Binding DisplayText}"
TextAlignment="Center"/>
</UserControl>
And it'll look like this:

Force ListView to refresh or Invalidate in UWP

I have a ListView with ItemTemplate. I want to bind one control background in ItemTemplate to 2 properties, one of properties is in ItemsSource and onother one is in my page. since UWP has no multibinding support, I bind it to one property in ItemSource and for another property in my page I want to handle it in my code behind.
<ListView >
<ListView.ItemTemplate>
<DataTemplate>
<Border HorizontalAlignment="Stretch"
x:Name="myborder"
Padding="5,0,5,0"
Background="{Binding myProperty, Converter={StaticResource convertPropertyToBgColor },ConverterParameter=border}">
<StackPanel Padding="0,10,10,10"
Background="{Binding myProperty, Converter={StaticResource convertPropertyToBgColor},ConverterParameter=stack}">
<TextBlock Text="{Binding Text}">
</StackPanel>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
in the convertPropertyToBgColor I get the brush from Resources.
and in code behind when my second desired property is changed I Change My resources. so the brush I have used from resources get changed and because of that I want to call that converter again to refresh Background, I called updateLayout but it doesn't refresh my ListView and it doesn't call myConvereter again. How can I force ListView to recreate or refresh Items that it has made?
Generally you class should implement INotifyPropertyChanged, then once you change the property, usually in its setter you also call OnPropertyChanged event which will update your UI. There are plenty examples of that, here is one.
The other way, may be to call Bindings.Update(), but normally you probably should use the method above.
To make my comments clearer - something like this is possible:
<StackPanel Orientation="Horizontal" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView x:Name="myList" Width="200">
<ListView.ItemTemplate>
<DataTemplate>
<Border HorizontalAlignment="Stretch"
x:Name="myborder"
Padding="5,0,5,0"
Background="{Binding Path=DataContext.MyProperty, ElementName=myList}">
<StackPanel Padding="0,10,10,10">
<TextBlock Text="{Binding}"/>
</StackPanel>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
<x:String>Element 1</x:String>
<x:String>Element 2</x:String>
<x:String>Element 3</x:String>
</ListView>
<Button Content="Change" Click="Button_Click"/>
</StackPanel>
code behind:
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
void RaiseProperty(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
private SolidColorBrush myPropety = new SolidColorBrush(Colors.Red);
public SolidColorBrush MyProperty
{
get { return myPropety; }
set { myPropety = value; RaiseProperty(nameof(MyProperty)); }
}
public MainPage()
{
this.InitializeComponent();
this.DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e) => MyProperty = new SolidColorBrush(Colors.Blue);
}

List of objects and ComboBoxes

Here's my problem:
I've got these classes:
public class CsvField
{
public string Content { get; set; }
public CsvField(string content)
{
Content = content;
}
}
public class CsvLine
{
public List<CsvField> Fields = new List<CsvField>();
public int LineNumber;
}
public static class Settings
{
public static List<string> Tags = new List<string>();
public static CsvLine AllHeaders = new CsvLine();
}
What I want to do, is display the ListBox containing every member of Settings.AllHeaders.Fields and a ComboBox containing all members of Settings.Tags list (placed horizontally - a member of AllHeaders on the left and a ComboBox next to it). So if I had 4 headers, I would get a List of those 4 headers and 4 ComboBoxes, each of them next to individual header. Each of these ComboBoxes would contain a list of tags.
So, I defined a DataTemplate:
<Window x:Class="CSV_To_Tags_App.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="clr-namespace:CSV_To_Tags_App"
Title="Window2" Height="435" Width="566">
<Window.Resources>
<DataTemplate DataType="{x:Type loc:CsvField}">
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="HeaderTextBlock" HorizontalAlignment="Left" TextWrapping="Wrap"
VerticalAlignment="Top" Text="{Binding Content}"
/>
<ComboBox HorizontalAlignment="Right" VerticalAlignment="Top" Width="120"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<Label Content="Available headers" HorizontalAlignment="Left"
VerticalAlignment="Top"/>
<ListBox x:Name="HeadersListtListBox" HorizontalAlignment="Left"
Height="254" Margin="36,104,0,0" VerticalAlignment="Top" Width="452"
ItemsSource="{Binding}"/>
</Grid>
</Window>
Now, XAML code above is incomplete, because I don't know how to:
1. Bind TextBlock to Settings.AllHeaders.Fields.Content
2. Bind ComboBox to Tags List
The link provided by #MD's does provide the solution to what you are asking in your question, but you have to rearrange things a little bit to see it.
The XAML below will give you what you are asking for, assuming your class that you are binding to is set up properly for data binding (implementing the INotifyPropertyChanged interface). There are lots of examples on this site of how to properly implement that portion.
<Window x:Class="CSV_To_Tags_App.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="clr-namespace:CSV_To_Tags_App"
Title="Window2" Height="435" Width="566">
<Grid>
<StackPanel Orientation="Horizontal">
<ItemsControl ItemsSource="{Binding Path=Settings.AllHeadings.Fields}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Content}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl ItemsSource="{Binding Path=Settings}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=Tags}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
</Window>

Adding buttons to WPF from ObservableCollection using MVVM pattern

I'm trying to add buttons to UserControl. I have to follow MVVM pattern. I have created a class, DeviceButton, with different set/gets and a constructor. In a viewmodel, used as datacontext, is the ObservableCollection and a get-method for the collection. I have bound the collection to a ItemsControl source and tried to add a template. I guess I'm missing something 'cause the buttons won't load.
The UserControl is added into a tab (using dragablz) which as well is a part of a ObservableCollection, also added at run time (this is working just fine). The idea is, that the tab has a list of buttons that has to be created at run time, where the list is fetched from a web service - so the buttons has to be added dynamically/programatically. Overview is just the first tab - a template for each tab (reflecting the fetched items) is being implemented when the buttons work. For now, I'm just adding a test button to the collection, but as stated, this won't show. What am I missing?
I have a Overview.xaml file:
<UserControl // attributes omitted to save spaces... ask if needed>
<StackPanel>
<Border>
<TextBlock FontSize="16" FontWeight="Bold" TextWrapping="WrapWithOverflow"
TextAlignment="Center" HorizontalAlignment="Center"
Foreground="{DynamicResource AccentColorBrush}">
Welcome to Greenhouse App
</TextBlock>
</Border>
<ItemsControl ItemsSource="{Binding DeviceButtons}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type vmodel:DeviceButton}">
<Button Width="50" Height="50" Margin="10 10 0 0" Command="{Binding OpenTab}"
CommandParameter="{Binding DeviceType}" HorizontalAlignment="Left"
VerticalAlignment="Top" Style="{DynamicResource SquareButtonStyle}">
<StackPanel>
<Image Source="{Binding ImageUrl}" />
</StackPanel>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
// Manually added test button...
<Button x:Name="windMill" HorizontalAlignment="Left" Margin="10,0,0,0"
VerticalAlignment="Top" Width="50" Height="50"
Command="{Binding OpenTab}" FontFamily="Segoe Ui"
Style="{DynamicResource SquareButtonStyle}">
<StackPanel>
<Image Source="/Greenhouse;component/Icons/Windmill.png" />
</StackPanel>
</Button
</StackPanel>
I'm trying to add an ObservableCollection of type DeviceButton, the _deviceButtons collection (Binded as ItemsSource to the ItemsControl)
The tabList items are working just find, and I can manually add more if needed (these will later be added through the OpenNewTab-command, which should be bound to the buttons)
The DeviceButton file:
public class DeviceButton
{
private readonly string _content;
private readonly string _deviceType;
private readonly string _imageUrl;
public DeviceButton(string content, string deviceType, string imageUrl)
{
_content = content;
_deviceType = deviceType;
_imageUrl = imageUrl;
}
public string Content
{
get { return _content; }
}
public string DeviceType
{
get { return _deviceType; }
}
public string ImageUrl
{
get { return _imageUrl; }
}
}
The collection is located in a viewmodel file, MainWindowViewModel.cs:
public class MainWindowViewModel : ViewModelBase, INotifyPropertyChanged
{
public ICommand OpenTab { get; set; }
private string tabControl { get; set; } = "0";
private IInterTabClient _interTabClient;
private ObservableCollection<TabContent> _tabContents = new ObservableCollection<TabContent>();
private ObservableCollection<DeviceButton> _deviceButtons = new ObservableCollection<DeviceButton>();
public MainWindowViewModel()
{
_interTabClient = new MyInterTabClient();
DeviceButtons.Add(new DeviceButton("Windturbine", "windturbine", "/Greenhouse;component/Icons/Windmill.png"));
TabContents.Add(new TabContent("Overview", new Overview()));
OpenTab = new RelayCommand<object>(OpenNewTab);
}
private void OpenNewTab(object obj)
{
MessageBox.Show("Yo"); // Not yet implemented
RaisePropertyChanged(() => tabControl);
}
public ObservableCollection<TabContent> TabContents
{
get { return _tabContents; }
}
public ObservableCollection<DeviceButton> DeviceButtons
{
get { return _deviceButtons; }
}
public IInterTabClient InterTabClient
{
get { return _interTabClient; }
}
}
The buttons aren't loaded when I start the program (only the "test" button added manually in the WPF).
The UserControl, Overview, is considered a tab in another Controls.Metrowindow, MainWindow.xaml:
<Window.Resources>
<Style TargetType="{x:Type dragablz:TabablzControl}">
<Setter Property="CustomHeaderItemTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type viewmodel:TabContent}">
<TextBlock Text="{Binding Header}" />
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type viewmodel:TabContent}">
<ContentPresenter Margin="4" Content="{Binding Content}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<dragablz:TabablzControl SelectedIndex="{Binding tabControl}" ItemsSource="{Binding TabContents}" x:Name="InitialTabablzControl" Margin="4 0 4 4">
<dragablz:TabablzControl.InterTabController>
<dragablz:InterTabController InterTabClient="{Binding MyInterTabClient}" />
</dragablz:TabablzControl.InterTabController>
</dragablz:TabablzControl>
I guess it's an issue with the resources/binding in the Overview.xaml, but I've exhausted all suggested solutions I could find.
Issue was in binding
ItemsSource="{Binding Path=DataContext.TabContents,
RelativeSource={RelativeSource AncestorLevel=1,
AncestorType={x:Type Window}, Mode=FindAncestor}}"
I suspect the DataContext is being set to a TabContent object, which does not have a DeviceButtons property
I would suggest using a tool like Snoop to verify your DataContext is what you expect.

WPF datatemplateselector not getting called

Hello I'm trying to dynamically change datatemplate but my method SelectTemplate in class TreeViewItemTemplateSelector never getting called (I've checked it by debugger) :( please help me :)
Code from xaml MainWindow:
Code in code behind:
Your problem seems to be that your TreeViewCustomItem is inheriting from TreeViewItem. (As seen in http://pastebin.com/jnP2nWMF)
Removing that inheritance (and the dependency property) causes the template selector to fire fine. What were/are you trying to achieve with the node item?
Looking at the OutputWindow, I get this message:
System.Windows.Data Error: 26 : ItemTemplate and ItemTemplateSelector are ignored for items already of the ItemsControl's container type; Type='TreeViewCustomItem'
You don't have to have items inherit from the TreeViewItem in order to bind them to a TreeView, TreeViewItem is something that the TreeView uses to hold the data, and then the DataTemplate is used to present the data.
Move DataTemplates from TreeView.Resources to Window.Resources
<Window.Resources><DataTemplate x:Key="DefaultTemplate">
<TextBlock Text="{Binding Header}"></TextBlock>
</DataTemplate><DataTemplate x:Key="Regulation">
<TextBlock Text="{Binding Path=Header}" FontWeight="Bold"></TextBlock>
</DataTemplate>
<DataTemplate x:Key="Article">
<TextBlock Text="{Binding Path=Header}" Foreground="Green"></TextBlock>
</DataTemplate>
<local:TreeViewItemTemplateSelector x:Key="TemplateSelector" DefaultTemplate="{StaticResource DefaultTemplate}" ArticleTemplate="{StaticResource Article}" RegulationTemplate="{StaticResource Regulation}" />
and make change
<TreeView ItemTemplateSelector="{StaticResource TemplateSelector}" Height="409" HorizontalAlignment="Left" Margin="10,10,0,0" Name="treeView1" VerticalAlignment="Top" Width="277" ItemsSource="{Binding}"/>
Update code and we will see. I put similar code into VS and it works so we need to take a closer look. So i checked this and changed
public class TreeViewCustomItem
{
public string Header { get; set; }
}
and this
listmy = new ObservableCollection<TreeViewCustomItem> { new TreeViewCustomItem { Header = "xD" }, new TreeViewCustomItem { Header = "xxD" } };
//treeView1.ItemsSource = listmy;
this.DataContext = listmy;
public class selector : DataTemplateSelector
{
public DataTemplate RegulationTemplate { get; set; }
public DataTemplate DefaultTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
TreeViewCustomItem treeViewItem = item as TreeViewCustomItem;
if (treeViewItem.Header == "xD")
{
return RegulationTemplate;
}
else
{
return DefaultTemplate;
}
}
}
and in XAML looks like this
xmlns:local="clr-namespace:WpfApplication1.Views">
<Window.Resources>
<DataTemplate x:Key="DefaultTemplate">
<TextBlock Text="{Binding Header}"></TextBlock>
</DataTemplate>
<DataTemplate x:Key="Regulation">
<TextBlock Text="{Binding Path=Header}" FontWeight="Bold"></TextBlock>
</DataTemplate>
<local:selector x:Key="selector_" DefaultTemplate="{StaticResource DefaultTemplate}" RegulationTemplate="{StaticResource Regulation}"/>
</Window.Resources>
<Grid>
<TreeView Height="409" HorizontalAlignment="Left" Margin="10,10,0,0" Name="treeView1" VerticalAlignment="Top" Width="277"
ItemsSource="{Binding}" ItemTemplateSelector="{StaticResource selector_}"/>
</Grid>
And it works so my presumption is that problem is inside TreeViewCustomItem.

Categories