I posted a question yesterday but I think I failed to explain it correctly.
Let me try again.
So this is my goal:
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.
that would be my dirty working example
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;
}
}
}
<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
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>