ItemsControl inside my UserControl is not updating - c#

I'm having trouble with my UserControl with following source code:
[ContentProperty("SetContent")]
public partial class HeaderContainer : UserControl
{
// Header region
public FrameworkElement SetContent
{
get { return (FrameworkElement)GetValue(SetContentProperty); }
set { SetValue(SetContentProperty, value); }
}
public static readonly DependencyProperty SetContentProperty =
DependencyProperty.Register("SetContent", typeof(FrameworkElement),
typeof(HeaderContainer), new PropertyMetadata(null));
public HeaderContainer()
{
InitializeComponent();
DataContext = this;
}
}
This is my XAML
<StackPanel>
<Border>
<TextBlock Text="{Binding Header}" />
</Border>
<ContentPresenter Content="{Binding SetContent}"/>
</StackPanel>
And here is my Problem:
<c:HeaderContainer Header="List">
<ItemsControl ItemsSource="{Binding ObjectList}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding DisplayName}"/>
<TextBlock Text="{Binding SecondLine}" Foreground="Gray" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</c:HeaderContainer>
The Itemscontrol itself is working, also the UserControl, when I add content in XAML. The problem is, when I add Items to ObjectList while it's in the HeaderContainer - nothing happens. Where am I thinking wrong?

It is hard to determine the exact issue here because your code is incomplete. You have a SetContent dependency property, however your example usage doe not use it. I think your approach should be to sub-class ContentControl, adding your Header property to this.
Or ... just use the Silverlight Toolkit HeaderedContentControl which I think does exactly what you are trying to achieve!

Related

TabControl not finding data templates for TabControl items

I am binding a collection of TabViewModel items to a TabControl. Each of these has a header string property, and a content property of my own custom type BaseTabContentViewModel, an abstract class which each actual tab data viewmodel implements. Eg ValuationTabViewModel which is a sub-class of BaseTabContentViewModel.
I add the new TabViewModel to the Observable<TabViewModel> for the TabControl to pick up and it shows in the UI. I have overridden style templates for the layout of the tab control and header which work fine. The only trouble is the content doesn't find the template in my resource dictionary based on its type, it just displays the full qualified class name of the viewmodel, showing that it is not finding a default template for this class.
Why isn't the ValuationTabViewModel that is being displayed, finding the datatemplate for this type below?
My main view model.
public ObservableCollection<TabViewModel> DetailTabs { get; }
var valuationTab = new TabViewModel(DetailTabConstants.ValuationTab, new ValuationTabViewModel(_eventAggregator, _errorNotifier, _windsorContainer));
DetailTabs = new ObservableCollection<TabViewModel> { valuationTab };
Main XAML
<TabControl Margin="0,-2,0,0" x:Name="SelectionTabs" Style="{StaticResource DetailTabControl}" ItemsSource="{Binding DetailTabs}"
SelectedValue="{Binding SelectedTab, Mode=TwoWay}" ItemContainerStyle="{StaticResource DetailTabItem}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding Content}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
The content style template I want it to use
<DataTemplate x:Key="ValuationTabTemplate" DataType="{x:Type detailTabs1:ValuationTabViewModel}" >
<detailTabs:ValuationTab Margin="0,10,0,10" />
</DataTemplate>
And my Tab item ViewModel class
public class TabViewModel : ViewModelBase
{
private string _header;
private BaseTabContentViewModel _content;
public string Header
{
get => _header;
set
{
_header = value;
RaisePropertyChanged(nameof(Header));
}
}
public BaseTabContentViewModel Content
{
get => _content;
set
{
_content = value;
RaisePropertyChanged(nameof(Content));
}
}
public TabViewModel(string header, BaseTabContentViewModel viewModel)
{
Header = header;
Content = viewModel;
}
}
Remove the <TabControl.ContentTemplate> element and define an implicit DataTemplate (without an x:Key) for each type:
<TabControl Margin="0,-2,0,0" x:Name="SelectionTabs" Style="{StaticResource DetailTabControl}" ItemsSource="{Binding DetailTabs}"
SelectedValue="{Binding SelectedTab, Mode=TwoWay}" ItemContainerStyle="{StaticResource DetailTabItem}">
<TabControl.Resources>
<DataTemplate DataType="{x:Type detailTabs1:ValuationTabViewModel}">
<detailTabs:ValuationTab Margin="0,10,0,10" />
</DataTemplate>
</TabControl.Resources>
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}" />
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
I think your custom DataTemplate isn't being used because you have specified both a Key and a DataType for it, and the Key takes precedence.
As per the Microsoft docs:
... if you assign this DataTemplate an x:Key value, you are overriding the implicit x:Key and the DataTemplate would not be applied automatically
I would suggest removing the Key property and just using DataType:
<DataTemplate DataType="{x:Type detailTabs1:ValuationTabViewModel}">
...
</DataTemplate>
Also, as #mm8 implied, you are explicitly setting the ContentTemplate of your TabControl. You need to remove that from the XAML.

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);
}

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.

How to set different HorizontalAlignment to ListBoxItems

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

Categories