What I'm trying to achieve is somewhat easy in web applications but I have been struggling to do it in WPF.
I want to load a paragraph of text in WPF and replace some of its specific words with editable textboxes. How can I do that?
What is the best strategy for getting this in a right a clean way?
Update:
Consider the following text. I want to display it in WPF and instead of the bold words put some textboxes.
Do you know someone rich and famous? Is he confident, popular, and
joyful all of the time—the epitome of mainstream success? Or,
on the other hand, is he stressed, having second thoughts about his life choices, and unsure about the meaning of his life?
WPF and XAML, unlike HTML, are all about data.
The best way to think and reason about any XAML-based UI is to think about the data that you need to show and interact with.
In this case:
public class Word
{
public string Value { get; set; }
public bool IsEditable { get; set; }
}
would represent each of our words. Then you just need a List of these:
public class ViewModel
{
public List<Word> Words { get; private set; }
public ViewModel()
{
var editableWords = new[] { "on", "of" };
var text = "Do you know someone rich and famous? Is he confident, popular, and joyful all of the time—the epitome of mainstream success? Or, on the other hand, is he stressed, having second thoughts about his life choices, and unsure about the meaning of his life?";
this.Words =
text.Split(' ')
.Select(x => new Word
{
Value = x,
IsEditable = editableWords.Contains(x.ToLower())
})
.ToList();
}
}
Notice how I'm turning the text into a List<Word> and setting IsEditable where desired.
Now it's just a matter of using an ItemsControl to show these:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
</Window.Resources>
<ItemsControl ItemsSource="{Binding Words}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="5,2,5,2">
<TextBlock Text="{Binding Value}"
VerticalAlignment="Center"/>
<TextBox Text="{Binding Value}"
Visibility="{Binding IsEditable, Converter={StaticResource BoolToVis}}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
Finally, set the DataContext to an instance of our ViewModel:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
Result:
Notice that I'm not "touching" the UI in code at all, this is all just simple, simple properties and DataBinding.
Related
I get this error:- System.NullReferenceException: 'Object reference not set to an instance of an object.'
objectPlacement was null.
private void Button_Click(object sender, RoutedEventArgs e)
{
ObjectPlacement w = new ObjectPlacement() {Topmost = };// ObjectPlacement is new WPF window
objectPlacement.WindowStyle = WindowStyle.None;
settingpanel.Children.Add(objectPlacement);//settingpanel stack is panel name
w.Show();
}
It would be much more usual to define a usercontrol or datatemplate for whatever you're trying to show in your window. A window is a kind of content control. One way to think of a window ( or contentcontrol ) is something that shows you some UI. All the UI in a window is that content.
When you add window to a project it is templated out with a grid in it.
This is the content and everything you want to see in that window goes in it.
You could replace that grid with something else instead.
If you made that a contentpresenter then you can bind or set what that'll show to some encapsulated re-usable UI.
Usually the best way to encapsulate re-usable UI is as a usercontrol.
A datatemplate can reference a usercontrol.
It is not usually your entire UI for a window you want to switch out. But you can and that is occasionally useful - say if you want a generic way to show dialogs.
The usual way to write wpf is mvvm so most devs will want some mvvm way of switching out UI.
I'll show you some code might make the description clearer.
There are some corners cut in what follows, so this is illustrative. Don't just run with this for your next lead developer interview at a stock traders.
But, basically you click a button for Login you "navigate" to a LoginUC view. Click a button for User and you "navigate" to UserUC.
My mainwindow.
<Window.Resources>
<DataTemplate DataType="{x:Type local:LoginViewModel}">
<local:LoginUC/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:UserViewModel}">
<local:UserUC/>
</DataTemplate>
</Window.Resources>
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ItemsControl ItemsSource="{Binding NavigationViewModelTypes}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Name}"
Command="{Binding DataContext.NavigateCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
CommandParameter="{Binding VMType}"
/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ContentPresenter Grid.Column="1"
Content="{Binding CurrentViewModel}"
/>
</Grid>
</Window>
Notice the datatemplates which associate the type of a viewmodel with a usercontrol.
https://learn.microsoft.com/en-us/dotnet/desktop/wpf/data/data-templating-overview?view=netframeworkdesktop-4.8
What will happen is you present your data in a viewmodel to the UI via that contentpresenter and binding. That viewodel is then templated out into UI with your viewmodel as it's datacontext. The datacontext of a UserUC view will therefore be an instance of UserViewModel. Change CurrentViewModel to an instance of LoginViewModel and you get a LoginUC in your mainwindow instead.
The main viewmodel.
public class MainWindowViewModel : INotifyPropertyChanged
{
public string MainWinVMString { get; set; } = "Hello from MainWindoViewModel";
public ObservableCollection<TypeAndDisplay> NavigationViewModelTypes { get; set; } = new ObservableCollection<TypeAndDisplay>
(
new List<TypeAndDisplay>
{
new TypeAndDisplay{ Name="Log In", VMType= typeof(LoginViewModel) },
new TypeAndDisplay{ Name="User", VMType= typeof(UserViewModel) }
}
);
private object currentViewModel;
public object CurrentViewModel
{
get { return currentViewModel; }
set { currentViewModel = value; RaisePropertyChanged(); }
}
private RelayCommand<Type> navigateCommand;
public RelayCommand<Type> NavigateCommand
{
get
{
return navigateCommand
?? (navigateCommand = new RelayCommand<Type>(
vmType =>
{
CurrentViewModel = null;
CurrentViewModel = Activator.CreateInstance(vmType);
}));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Type and display relates the type for a viewmodel with text displayed in the UI.
public class TypeAndDisplay
{
public string Name { get; set; }
public Type VMType { get; set; }
}
This is "just" quick and dirty code to illustrate a principle which is usually called viewmodel first navigation. Google it, you should find a number of articles explaining it further.
For completeness:
<UserControl x:Class="wpf_Navigation_ViewModelFirst.LoginUC"
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:local="clr-namespace:wpf_Navigation_ViewModelFirst"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel Background="Yellow">
<TextBlock Text="This is the Login User Control"/>
<TextBox>
<TextBox.InputBindings>
<KeyBinding Key="Return" Command="{Binding LoginCommand}"/>
</TextBox.InputBindings>
</TextBox>
</StackPanel>
</UserControl>
public class LoginViewModel
{
private RelayCommand loginCommand;
public RelayCommand LoginCommand
{
get
{
return loginCommand
?? (loginCommand = new RelayCommand(
() =>
{
string s = "";
}));
}
}
}
<UserControl x:Class="wpf_Navigation_ViewModelFirst.UserUC"
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:local="clr-namespace:wpf_Navigation_ViewModelFirst"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid Background="pink">
<TextBlock Text="This is the User module Control"
VerticalAlignment="Top"
/>
<TextBlock Text="{Binding Path=DataContext.MainWinVMString, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
VerticalAlignment="Bottom"
/>
</Grid>
</UserControl>
public class UserViewModel
{
}
I put this together some years ago, I would now recommend the community mvvm toolkit with it's code generation, base classes, messenger etc.
I am working on trying to learn and understand WPF TreeView and Data Binding in XAML. I cannot seem to grasp the concept of data binding and your help is much appreciated.
The ISSUE is nothing displays in the TreeView. I am mostly interested in understanding how to correctly bind data in WPF to objects for the implementation of a tree view.
Concept: consider a SubjectList with a set of Subjects. Each Subject only has ONE student for the sake of this example
Expected Output in TreeView
Maths
Student 1
Science
Student 2
Arts
Student 3
My current XAML attempt is based on the tutorial read here http://blogs.microsoft.co.il/pavely/2014/07/12/data-binding-for-a-wpf-treeview/.
Following this I have the following XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<TreeView x:Name="treeView" HorizontalAlignment="Left"
Height="284"
Margin="18,10,0,0"
VerticalAlignment="Top"
Width="115"
ItemsSource="{Binding SubjectList}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding SubjectList}"
DataType="{x:Type local:Subject}">
<TreeViewItem Header="{Binding SubjectName}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</StackPanel>
MainWindow function to add Student Objects into a StudentList:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Student s1 = new Student("Alex", 1);
Student s2 = new Student("Kevin", 2);
Student s3 = new Student("Sina", 3);
Student s4 = new Student("Evan", 4);
Subject a1 = new Subject("Maths", 1);
Subject a2 = new Subject("Science", 2);
Subject a3 = new Subject("Arts", 3);
a1.setStudent(s1);
a2.setStudent(s2);
a3.setStudent(s3);
Subjects list = new Subjects();
list.AddSubjects(a1);
list.AddSubjects(a2);
list.AddSubjects(a3);
DataContext = list;
}
}
Classes
class Subjects
{
private List<Subject> subjectList;
public List<Subject> SubjectList { get; set;}
public Subjects()
{
SubjectList = new List<Subject>();
}
public void AddSubjects(Subject s)
{
SubjectList.Add(s);
}
}
class Subject
{
private String subjectName;
private List<Student> studentList;
//accessor methods
public String SubjectName { get; set; }
public List<Student> StudentList { get; }
public Subject()
{
}
public Subject(string name, int id)
{
SubjectName = name;
StudentList = new List<Student>();
}
public void setStudent(Student aStudent)
{
StudentList.Add(aStudent);
}
}
class Student
{
public Student()
{
}
public Student(string name, int id)
{
StudentName = name;
StudentID = id;
}
private String studentName;
private int studentID;
//accessor methods
public String StudentName { get; set;}
public int StudentID { get; set; }
}
}
What am i doing wrong? Anyone who can point me in the right direction of better understanding the concept of data binding in WPF to a list of objects would be a massive help in my self study!
Update 1: setting DataContext = list; and removing the DataContext reference in XAML resolved the issue of having two View's defined.
Question 2:: I am still a bit confused with View's. I have added a List as a property of a Subject Class.
How would you retrieve the Student Name from the List of Student objects which are within a Subject Object in XAML? Do you require a View for any/every collection you wish to show in the TreeView? I wish to learn how these pieces all work together. Any further material or assistance greatly appreciated.
You are creating two instances of your Subjects view model, one in XAML
<Window.DataContext>
<local:Subjects/>
</Window.DataContext>
and one in code
Subjects list = new Subjects();
Adding items to the list instance in code behind won't add them to the instance in the DataContext.
Change your code like this:
var list = (Subjects)DataContext;
list.AddSubjects(a1);
...
Or remove the DataContext assignment from your XAML and write the code behind like this:
var list = new Subjects();
list.AddSubjects(a1);
...
DataContext = list;
That said, it may make sense to use ObservableCollection instead of List to notify about collection changes, e.g. while adding or removing subjects.
So I believe your confusion is rooted in the concept of binding so I will give a crack at trying to clarify it for you.
Take this code you posted:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<TreeView x:Name="treeView" HorizontalAlignment="Left"
Height="284"
Margin="18,10,0,0"
VerticalAlignment="Top"
Width="115"
ItemsSource="{Binding SubjectList}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding SubjectList}"
DataType="{x:Type local:Subject}">
<TreeViewItem Header="{Binding SubjectName}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</StackPanel>
In your code behind you set the datacontext to an instance of Subjects. That means the window's datacontext is Subjects.
Now as you go deeper into the xaml you reach the stackpanel. This inherits the same datacontext so the stackpanel's datacontext is still Subjects.
Now you get to the treeview. The treeview's datacontext is still Subjects which is why you are able to bind the ItemsSource property to SubjectList, a property on the Subjects class.
Now when you get to the TreeView.ItemTemplate, the datacontext is a single item from SubjectList, aka an instance of Subject.
The HierachicalDataTemplate is still using the same context of an instance of Subject. You can't bind the HierarchicalDataTemplate's ItemsSource to SubjectList because the Subject class does not have that property.
You can bind to StudentList though since the Subject class does have that property.
If you were to bind the ItemsSource to StudentList you could then inside the HierarchicalDataTemplate put a TextBox with the binding to subject name since inside of the HierachicalDataTemplate has the DataContext of a singular StudentItem
So finally the working code would look like this:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<TreeView x:Name="treeView" HorizontalAlignment="Left"
Height="284"
Margin="18,10,0,0"
VerticalAlignment="Top"
Width="115"
ItemsSource="{Binding SubjectList}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding StudentList}">
<TextBox Text="{Binding StudentName}"/>
<TextBox Text=" "/>
<TextBox Text="{Binding ID}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</StackPanel>
So all in all you were very close, but I hope this helps you understand what is going on when you go deeper into the bindings. If you are confused about anything leave a comment and I will try to make it clearer
I'm still pretty new to WPF and I decided to change the application I am developing to start following the MVVM pattern as best as I could. I am running into a problem when I try to have a list box dictate the view model of a content control. I've been stuck on this for a while and searching the internet is not producing answers for me.
For some reason a new instance of the view model the list box contains is being generated as the data context of the content control. When I was debugging I made sure that the list box contains the view models it should, and that the item I select on the list box is indeed the item that the list box is selecting, however the content control changing based on the selection. There is a view model populating the content control, however it is not in the collection the list box populates from. And I can somehow delete the view model in the content control via my remove button. But when I make a selection change on the list box, or add a new item to the collection it populates the content control with a new view model that once again is not in the collection. I have no clue why it is doing this, or what in my code would suggest this behavior.
I made a simple application to try and figure out what I'm doing wrong. It replicates my problem perfectly. I'm pretty sure the buttons don't adhere to MVVVM (supposed to run a command contained in the view model to adhere to MVVM from what I've been reading) but that is not my main concern right now as the problem exists without the buttons.
MainWindow.xml
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1" x:Class="WpfApplication1.MainWindow"
Title="MainWindow" Height="440" Width="436">
<Window.DataContext>
<local:mwvm/>
</Window.DataContext>
<Window.Resources>
<DataTemplate DataType="{x:Type local:ucvm}">
<local:uc/>
</DataTemplate>
</Window.Resources>
<Grid>
<Button Content="a" HorizontalAlignment="Left" Margin="323,351,0,0" VerticalAlignment="Top" Width="95" Click="Button_Click"/>
<Button Content="r" HorizontalAlignment="Left" Margin="323,378,0,0" VerticalAlignment="Top" Width="95" Click="Button_Click_1"/>
<ContentControl Margin="10,10,110,10" Content="{Binding SelectedItem, ElementName=lb_UCs}"/>
<ListBox x:Name="lb_UCs" HorizontalAlignment="Left" Height="336" Margin="323,10,0,0" VerticalAlignment="Top" Width="95" ItemsSource="{Binding UCs}" DisplayMemberPath="CoolText"/>
</Grid>
</Window>
MainWindow.xaml.cs
public partial class PanelPartsView : UserControl
{
private PanelPartsViewModel _DC;
public PanelPartsView()
{
InitializeComponent();
_DC = DataContext as PanelPartsViewModel;
}
private void btn_Remove_Click(object sender, RoutedEventArgs e)
{
_DC.Panels.Remove(lb_Panels.SelectedItem as PartsViewModel);
}
private void btn_Add_Click(object sender, RoutedEventArgs e)
{
var pvm = new PartsViewModel();
_DC.Panels.Add(pvm);
lb_Panels.SelectedItem = pvm;
System.Console.WriteLine("lb_Panels.selecteditem = {0}", ((PartsViewModel)lb_Panels.SelectedItem).PanelName);
System.Console.WriteLine("cc_PanelParts.content = {0}", ((PartsViewModel)cc_PanelParts.Content).PanelName);
}
}
mwvm
class mwvm
{
private ObservableCollection<ucvm> _UCs = new ObservableCollection<ucvm>();
public ObservableCollection<ucvm> UCs
{
get { return _UCs; }
}
public mwvm()
{
//this is for for testing, the real application would be purely dynamic
_UCs.Add(new ucvm());
_UCs.Add(new ucvm());
_UCs.Add(new ucvm());
}
}
uc.xaml
<UserControl
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:local="clr-namespace:WpfApplication1" x:Class="WpfApplication1.uc"
mc:Ignorable="d" d:DesignWidth="300" Height="90">
<Grid>
<Grid.DataContext>
<local:ucvm/>
</Grid.DataContext>
<Button Content="{Binding CoolText}" Margin="10,10,10,0" Height="44" VerticalAlignment="Top"/>
<TextBox Height="23" Margin="10,59,10,0" TextWrapping="Wrap" Text="{Binding CoolText}" VerticalAlignment="Top"/>
</Grid>
</UserControl>
uc.xaml.cs
public partial class uc : UserControl
{
public uc()
{
InitializeComponent();
}
}
ucvm.cs
class ucvm : INotifyPropertyChanged
{
private static int i = 1;
private string _CoolText = "<" + i++ + ">" + System.DateTime.Now.ToLongTimeString();
public string CoolText
{
get { return _CoolText; }
set
{
_CoolText = value;
NPC("CoolText");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NPC(string s)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(s));
}
}
I have also tried binding the content control like so...
<ContentControl Content="{Binding SelectedUCVMl, Mode=OneWay}"/>
<ListBox x:Name="lb_UCs" ItemsSource="{Binding UCs}" SelectedItem="{Binding SelectedUCVM}" DisplayMemberPath="CoolText"/>
...and so...
<ContentControl Content="{Binding UCs/}"/>
<ListBox x:Name="lb_UCs" ItemsSource="{Binding UCs}" IsSynchronizedWithCurrentItem="True" DisplayMemberPath="CoolText"/>
but to no avail.
Any help would be greatly appreciated.
It looks like you just have to remove this part from uc.xaml:
<Grid.DataContext>
<local:ucvm/>
</Grid.DataContext>
This syntax creates a new instance of the view model, each time an instance of uc.xaml is created, which of course isn't what you want. You want the data context of uc.xaml instances to inherit the instance currently selected in the list box.
I have an ObservableCollection<IRuleCondition> that I want to display - the IRuleCondition interface is used by 2 different classes I want to display, a RuleCondition that simply displays one rule condition (info such as priority, property to check and so on), and a RuleConditionGroup, that can contain 2 or more RuleConditions, grouped in such a way that any of the conditions could match, or all etc.
In the XAML I was wondering is there a way to display a different ListView.ItemTemplate depending on what the type is that it encounters in the ObservableCollection<IRuleCondition>? Or would I need to implement two different ObservableCollections?
Here is a simple example of how this works
This is how the objects are defined
public interface Person
{
string Name { get; set; }
}
public class Manager : Person
{
public string Name { get; set; }
}
public class Employee : Person
{
public string Name { get; set; }
public string ManagerName { get;set;}
}
This is the MainWindow code behind
public partial class MainWindow : Window
{
ObservableCollection<Person> mPeople = new ObservableCollection<Person>();
public ObservableCollection<Person> People
{
get
{
return mPeople;
}
}
public MainWindow()
{
DataContext = this;
mPeople.Add( new Employee{ Name = "x" , ManagerName = "foo"});
mPeople.Add( new Manager { Name = "y"});
InitializeComponent();
}
}
This is the MainWindow XAML
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:WpfApplication1"
Title="MainWindow"
Height="350"
Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type my:Employee}">
<StackPanel Background="Green" Width="300">
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding ManagerName}" />
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type my:Manager}">
<StackPanel Background="Red"
Width="300">
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding People}"></ListBox>
</Grid>
</Window>
As you can see there are two datatemplates one for Manager and one for Employee
And this is how the crappy output looks like. Notice the green and red background and extra field displayed for the Employee compared to the manager
Just define two different DataTemplates in the Resources section, one for each RuleCondition type.
1) Create your two different data templates, just as you say you've already done.
2) Create a custom DataTemplateSelector to choose the appropriate template.
One of your comments states that you're getting an error from your DataTemplateSelector. Verify that you're implementing the class correctly, perhaps paste your implementation. It should be fairly small and straightforward.
I'm having trouble understanding how to databind my Songs List<> to a ListBox without needing to set the ItemsSource in the code behind.
It works though, but I would really like to see the List working in the liveview Designer.
namespace App5
{
class SongsData
{
public string Title { get; set; }
public string Lyrics { get; set; }
}
}
And in my MainPage.xaml.cs:
public MainPage()
{
this.InitializeComponent();
List Songs = new List();
Songs.Add(new SongsData() { Title = "Your Song", Lyrics = "It's a little bit funny.." });
Songs.Add(new SongsData() { Title = "Rocket Man", Lyrics = "I'm the Rocket Maaaan.." });
SongsListBox.ItemsSource = Songs;
}
And in the XAML I have a basic ListBox:
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Can a friendly person please help me understand what to change - and hopefully why - to get the songs title to show in the ListBox in the liveview Designer of Visual Studio?
With the above I have to Debug the program to see the song titles in the ListBox.
Many thanks in advanced.
You basically need to apply the DesignData build time action to your data files. A very comprehensive walkthrough can be found at msdn.
A quick and simple solution is to move your ListBox to a new UserControl, put the list initialization in the UserControl's constructor, and then add an instance of the UserControl to your main form.
Example:
SongListControl.cs :
namespace App5
{
public parital class SongListControl : userControl
{
this.InitializeComponent();
List Songs = new List();
Songs.Add(new SongsData() { Title = "Your Song", Lyrics = "It's a little bit funny.." });
Songs.Add(new SongsData() { Title = "Rocket Man", Lyrics = "I'm the Rocket Maaaan.." });
SongsListBox.ItemsSource = Songs;
}
}
SongListControl.xaml :
<UserControl x:Class="App5.SongListControl"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</UserControl>
Then in your main window :
<Window x:Class="App5.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:app="clr-namespace:App5"
Title="MainWindow" Height="350" Width="525">
<Grid>
<app:SongListControl />
</Grid>
</Window>
When you build the project, the constructor initialization will occur in MainWindow preview.