What's wrong with this picture?
Instead of displaying a nice picture of a prehistoric plant, the string of the location of the bitmap is being displayed!
Here's the XAML (snippet):
<DataTemplate x:Key="YoungPicCell">
<StackPanel Orientation="Horizontal">
<Image Height="200" Width="200" Stretch="None" Source="{Binding Path=YoungPicBmp}" />
</StackPanel>
</DataTemplate>
The filenames (and other data) are loaded at runtime from an XML file.
Here is the data being loaded from the XML file at runtime:
public class LVData
{
public string Name { get; set; }
public string YoungPic { get; set; }
public BitmapSource YoungPicBmp { get { return new BitmapImage(new Uri("{YoungPic}")); } }
public string MediumPic { get; set; }
public BitmapSource MediumPicBmp { get { return new BitmapImage(new Uri("{MediumPic}")); } }
public string AdultPic { get; set; }
public BitmapSource AdultPicBmp { get { return new BitmapImage(new Uri("{AdultPic}")); } }
public bool SaltWater { get; set; }
public bool FreshWater { get; set; }
public bool Grasslands { get; set; }
public bool Swamp { get; set; }
public bool TropicalForest { get; set; }
public bool Forest { get; set; }
public bool ForestEdge { get; set; }
public bool Sand { get; set; }
public bool Coastal { get; set; }
public bool RiverBorder { get; set; }
public bool LakeBorder { get; set; }
public bool Floodplain { get; set; }
}
public class WindowViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
//called when a property is changed
protected void RaisePropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
private ObservableCollection<LVData> _plantList = new ObservableCollection<LVData>();
public ObservableCollection<LVData> lsvData
{
get { return _plantList; }
set { _plantList = value; RaisePropertyChanged("lsvData"); }
}
public void PopulateDataFromXML(string filename)
{
XDocument loaded = XDocument.Load(#"DinoIslandPlants.xml");
var Plants = from x in loaded.Descendants("Plants")
select new
{
Name = x.Descendants("Name").First().Value,
YoungPic = x.Descendants("YoungPic").First().Value,
MediumPic = x.Descendants("MediumPic").First().Value,
AdultPic = x.Descendants("AdultPic").First().Value,
SaltWater = x.Descendants("SaltWater").First().Value,
FreshWater = x.Descendants("FreshWater").First().Value,
Grasslands = x.Descendants("Grasslands").First().Value,
Swamp = x.Descendants("Swamp").First().Value,
TropicalForest = x.Descendants("TropicalForest").First().Value,
Forest = x.Descendants("Forest").First().Value,
ForestEdge = x.Descendants("ForestEdge").First().Value,
Sand = x.Descendants("Sand").First().Value,
Coastal = x.Descendants("Coastal").First().Value,
RiverBorder = x.Descendants("RiverBorder").First().Value,
LakeBorder = x.Descendants("LakeBorder").First().Value,
Floodplain = x.Descendants("Floodplain").First().Value
};
foreach (var _plant in Plants)
{
_plantList.Add(new LVData {
Name = _plant.Name,
YoungPic = _plant.YoungPic,
MediumPic = _plant.MediumPic,
AdultPic = _plant.AdultPic,
SaltWater = Convert.ToBoolean(_plant.SaltWater),
FreshWater = Convert.ToBoolean(_plant.FreshWater),
Grasslands = Convert.ToBoolean(_plant.Grasslands),
Swamp = Convert.ToBoolean(_plant.Swamp),
TropicalForest = Convert.ToBoolean(_plant.TropicalForest),
Forest = Convert.ToBoolean(_plant.Forest),
Sand = Convert.ToBoolean(_plant.Sand),
Coastal = Convert.ToBoolean(_plant.Coastal),
RiverBorder = Convert.ToBoolean(_plant.RiverBorder),
LakeBorder = Convert.ToBoolean(_plant.LakeBorder),
Floodplain = Convert.ToBoolean(_plant.Floodplain)
});
}
RaisePropertyChanged("lsvData");
}
}
When binding to an Image control you need to bind to a BitmapSource. This should be pretty straight forward. Change the type of the property (or add a new one) to BitmapSource and then in the get do something like this:
... get { return new BitmapImage(new Uri("{PathToImage}")); }
where PathToImage is a recognizable path to the image you want to display.
Related
I want to call a method for my WPF-App with subtype objects of my Piece class. My problem is that the subtype objects have more properties than e.g the the Text objects.
Do you know a way to cope with this better than I do in my FillForm example?
namespace Namespace
{
public abstract class Piece
{
public int id { get; set; }
public string title { get; set; }
public string description { get; set; }
}
public class Text : Piece
{
}
public class Image: Piece{
public string filePath { get; set; }
public string fileformat { get; set; }
}
public class Video : Image
{
}
}
}
Example method:
public void FillForm(Piece currentPiece)
{
pieceIdTextBox.Text = currentPiece.id.ToString();
pieceNameTextBox.Text = currentPiece.title;
pieceDescriptionTextBox.Text = currentPiece.description;
if (!currentPiece.GetType().ToString().Equals("Namespace.Text"))
{
pieceFileSelectURLTextBlock.Text = (currentPiece as Namespace.Image).filePath;
SetPreviews((currentPiece as Namespace.Image).filePath);
}
}
Thanks!
Why not just change the method to the following with more type-safety
public void FillForm(Piece currentPiece)
{
pieceIdTextBox.Text = currentPiece.id.ToString();
pieceNameTextBox.Text = currentPiece.title;
pieceDescriptionTextBox.Text = currentPiece.description;
if (currentPiece as Namespace.Image imagePiece)
{
pieceFileSelectURLTextBlock.Text = imagePiece.filePath;
SetPreviews(imagePiece.filePath);
}
}
Do a safecast:
public void FillForm(Piece currentPiece)
{
pieceIdTextBox.Text = currentPiece.id.ToString();
pieceNameTextBox.Text = currentPiece.title;
pieceDescriptionTextBox.Text = currentPiece.description;
var imagePiece = currentPiece as Image;
if(imagePiece != null)
pieceFileSelectURLTextBlock.Text = imagePiece .filePath;
SetPreviews(imagePiece .filePath);
}
}
I copied this code from another project and can't figure out why it isn't working. My observable collections are working great binding and updating, but my textboxes aren't changing. I have a button click that lets the user pick a directory (DirectoryBrowse() method) and then assigns that value to the data context's property that is bound to the textbox. PropertyChanged is always null and I can't figure out why! The initial binding works just fine, just note when I change the value in the code-behind. I've been at this entirely too long, but any help would be appreciated!
DataContext class:
[Serializable]
public class Settings : ViewModels.ViewModelEntity
{
public static Settings defaultSettings { get; set; }
private string _ExportDir;
public string ExportDir
{
get { return this._ExportDir; }
set
{
if (this._ExportDir != value)
{
this._ExportDir = value;
this.NotifyPropertyChanged("ExportDir");
}
}
}
private string _LastRunTime;
public string LastRunTime
{
get { return this._LastRunTime; }
set
{
if (this._LastRunTime != value)
{
this._LastRunTime = value;
this.NotifyPropertyChanged("LastRunTime");
}
}
}
private string _TSCertPath;
public string TSCertPath
{
get { return this._TSCertPath; }
set
{
if (this._TSCertPath != value)
{
this._TSCertPath = value;
this.NotifyPropertyChanged("TSCertPath");
}
}
}
public ObservableCollection<Map> Brokers { get; set; }
public ObservableCollection<Account> Accounts { get; set; }
public List<Holiday> Holidays { get; set; }
public bool RefreshHolidays { get; set; }
public string ProxyServer { get; set; }
public string ProxyPort { get; set; }
public string ProxyUsername { get; set; }
public string ProxyPassword { get; set; }
public bool TSProd { get; set; }
public string TSTriad { get; set; }
public string TSPassword { get; set; }
public string TSCertPassword { get; set; }
public Settings()
{
this.Brokers = new ObservableCollection<Map>();
this.Accounts = new ObservableCollection<Account>();
}
}
Xaml:
<TextBlock TextWrapping="Wrap" Text="File Export Path*"/>
<TextBox TextWrapping="Wrap" Text="{Binding Path=ExportDir, Mode=TwoWay}" />
<Button x:Name="btnBrowseExportDir" Content="..." Click="btnBrowseExportDir_Click"/>
Code-behind:
public MainWindow()
{
InitializeComponent();
Settings.Initialize();
this.DataContext = Settings.defaultSettings;
string[] args = Environment.GetCommandLineArgs();
if (args.Contains("create"))
{
this.Close();
}
}
private string DirectoryBrowse()
{
CommonOpenFileDialog dialog = new CommonOpenFileDialog();
dialog.IsFolderPicker = true;
CommonFileDialogResult result = dialog.ShowDialog();
if (result.ToString().ToUpper() == "OK")
{
if (!Directory.Exists(dialog.FileNames.First()))
{
this.lblStatus.Text = "Invalid directory selected";
return string.Empty;
}
else
{
return dialog.FileNames.First();
}
}
else
{
this.lblStatus.Text = "Invalid directory selected";
return string.Empty;
}
}
private void btnBrowseExportDir_Click(object sender, RoutedEventArgs e)
{
Settings.defaultSettings.ExportDir = DirectoryBrowse();
}
ViewModelEntity:
public class ViewModelEntity
{
public event PropertyChangedEventHandler PropertyChanged;
public virtual void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Settings.defaultSettings is never assigned a value. So the databinding have nothing to work with.
Thoug code for Settings.Initialize() is missing.
#Dave and #Icepickle showed me what I was missing, no implementaiton of INotifyPropertyChanged!
I want to split the column creation of a datagrid partially in c# coee and partially in xaml code. my datagrid holds columns starting from 1 - 100 ,which are created using a list in c#.
Now i want to add columns in datagrid whose source is an object field of a class which have 30 properties. Please refer the code.
public partial class MainWindow : Window
{
private List<Test> listTest;
Test obj;
public MainWindow()
{
InitializeComponent();
}
public List<Test> ListTest
{
get
{
return listTest;
}
set
{
listTest = value;
}
}
private void button1_Click(object sender, RoutedEventArgs e)
{
listTest = new List<Test>();
for (int i = 0; i < 10; i++)
{
obj = new Test();
listTest.Add(obj);
}
this.MyDatagrid.ItemsSource = ListTest; //creating columns in datagrid by list
}
}
public class Test
{
public string m_field1_Test{get;set;}
public string m_field2_Test { get; set; }
public string m_field3_Test { get; set; }
public string m_field4_Test { get; set; }
public string m_field5_Test { get; set; }
public string m_field6_Test { get; set; }
public string m_field7_Test { get; set; }
public string m_field8_Test { get; set; }
public string m_field9_Test { get; set; }
public string m_field10_Test { get; set; }
public Test1 test1obj { get; set; }
public Test()
{
m_field1_Test = "field1";
m_field2_Test = "field2";
m_field3_Test = "field3";
m_field4_Test = "field4";
m_field5_Test = "field5";
m_field6_Test = "field6";
m_field7_Test = "field7";
m_field8_Test = "field8";
m_field9_Test = "field9";
m_field10_Test = "field10";
test1obj = new Test1();
}
}
public class Test1
{
public string m_field1_Test1 { get; set; }
public string m_field2_Test1 { get; set; }
public string m_field3_Test1 { get; set; }
public string m_field4_Test1 { get; set; }
public string m_field5_Test1 { get; set; }
public string m_field6_Test1 { get; set; }
public string m_field7_Test1 { get; set; }
public string m_field8_Test1 { get; set; }
public string m_field9_Test1 { get; set; }
public string m_field10_Test1 { get; set; }
public string m_field11_Test1 { get; set; }
public string m_field12_Test1 { get; set; }
public string m_field13_Test1 { get; set; }
public string m_field14_Test1 { get; set; }
public string m_field15_Test1 { get; set; }
public string m_field16_Test1 { get; set; }
public string m_field17_Test1 { get; set; }
public string m_field18_Test1 { get; set; }
public string m_field19_Test1 { get; set; }
public string m_field20_Test1 { get; set; }
public string m_field21_Test1 { get; set; }
public string m_field22_Test1 { get; set; }
public string m_field23_Test1 { get; set; }
public string m_field24_Test1 { get; set; }
public string m_field25_Test1 { get; set; }
public string m_field26_Test1 { get; set; }
public string m_field27_Test1 { get; set; }
public string m_field28_Test1 { get; set; }
public string m_field29_Test1 { get; set; }
public string m_field30_Test1 { get; set; }
public Test1()
{
m_field1_Test1 = "field1";
m_field2_Test1 = "field2";
m_field3_Test1 = "field3";
m_field4_Test1 = "field4";
m_field5_Test1 = "field5";
m_field6_Test1 = "field6";
m_field7_Test1 = "field7";
m_field8_Test1 = "field8";
m_field9_Test1 = "field9";
m_field10_Test1 = "field10";
m_field11_Test1 = "field11";
m_field12_Test1 = "field12";
m_field13_Test1 = "field13";
m_field14_Test1 = "field14";
m_field15_Test1 = "field15";
m_field16_Test1 = "field16";
m_field17_Test1 = "field17";
m_field18_Test1 = "field18";
m_field19_Test1 = "field19";
m_field20_Test1 = "field20";
m_field21_Test1 = "field21";
m_field22_Test1 = "field22";
m_field23_Test1 = "field23";
m_field24_Test1 = "field24";
m_field25_Test1 = "field25";
m_field26_Test1 = "field26";
m_field27_Test1 = "field27";
m_field28_Test1 = "field28";
m_field29_Test1 = "field29";
m_field30_Test1 = "field30";
}
}
Now i want to display these 30 fields of Test1 class as the columns of datagrid. I already know that this can be done in c# code using LINQ. But in that case i have to create a new list which will have all items of list as well as fields of object. I want to know a way out by which i dnt have to manually do that.
Can somebody know the solution...
Use Databinding to bind your List to your DataGrid.
I'll also recommend the MVVM design pattern to make your app design simplier.
To programatically add a column:
DataGridTextColumn textColumn = new DataGridTextColumn();
textColumn.Header = "First Name";
textColumn.Binding = new Binding("FirstName");
dataGrid.Columns.Add(textColumn);
Check out this post on the WPF DataGrid discussion board for more information.
The simple solution called Reflection!
Lets say you have a class called Bar:
public class Bar
{
string bar1 = "bar1", bar2 = "bar2", bar3 = "bar3";
public string Bar1
{
get { return bar1; }
set { bar1 = value; }
}
public string Bar2
{
get { return bar2; }
set { bar2 = value; }
}
public string Bar3
{
get { return bar3; }
set { bar3 = value; }
}
}
and a class called Foo which has an instance of Bar:
public class Foo
{
string foo1 = "foo1", foo2 = "foo2", foo3 = "foo3";
Bar bar1 = new Bar();
public Bar Bar1
{
get { return bar1; }
set { bar1 = value; }
}
public string Foo1
{
get { return foo1; }
set { foo1 = value; }
}
public string Foo2
{
get { return foo2; }
set { foo2 = value; }
}
public string Foo3
{
get { return foo3; }
set { foo3 = value; }
}
}
and on your Form1 you have a datagridview (i dont post the code of the designer), then you can do it with reflection (the code is unslightly written in Form1)
public partial class Form1 : Form
{
List<Foo> foo = new List<Foo>();
public List<Foo> Foo
{
get { return foo; }
set { foo = value; }
}
public Form1()
{
InitializeComponent();
foo.Add(new WindowsFormsApplication1Foo());
foo.Add(new WindowsFormsApplication1.Foo());
BindingSource source = new BindingSource();
source.DataSource = Foo;
dataGridView1.DataSourceChanged += new EventHandler(dataGridView1_DataSourceChanged);
dataGridView1.DataSource = Foo.ToArray();
}
bool sourceChange = true;
void dataGridView1_DataSourceChanged(object sender, EventArgs e)
{
if (sender is DataGridView)
{
DataGridView dgv = (DataGridView)sender;
if (sourceChange)
{
sourceChange = false;
object source = dgv.DataSource;
Type sourceType = source.GetType();
if (sourceType.IsArray)
{
Array arr = (Array)source;
if (arr.Length > 0)
{
Type elementType = sourceType.GetElementType();
Type myType = CreateCustomType(elementType);
List<object> list = new List<object>();
IEnumerator enumerator = arr.GetEnumerator();
while (enumerator.MoveNext())
{
object myNewTypeInstance=null;
CopyData(enumerator.Current, myType,ref myNewTypeInstance);
list.Add(myNewTypeInstance);
}
dgv.DataSource = list;
}
}
}
}
}
private void CopyData(object oldObj, Type newType,ref object newObj)
{
if(newObj==null)
newObj = Activator.CreateInstance(newType);
Type oldType = oldObj.GetType();
foreach (var item in oldType.GetProperties())
{
string name = item.Name;
object value = oldType.GetProperty(item.Name).GetValue(oldObj,null);
if (item.PropertyType.Namespace != "System")
{
CopyData(value, newType,ref newObj);
}
else
{
newType.GetProperty(name).SetValue(newObj, value, null);
}
}
}
private Type CreateCustomType(Type t)
{
AppDomain myDomain = Thread.GetDomain();
AssemblyName myAsmName = new AssemblyName() { Name = "MyDynamicAssembly" };
AssemblyBuilder myAsmBuilder = myDomain.DefineDynamicAssembly(myAsmName, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder myModBuilder = myAsmBuilder.DefineDynamicModule(myAsmName.Name, myAsmName.Name + ".dll");
TypeBuilder myTypeBuilder = myModBuilder.DefineType("MyDynamicClass", TypeAttributes.Public);
DefineProperties(myTypeBuilder, t);
return myTypeBuilder.CreateType();
}
private void DefineProperties(TypeBuilder tBuilder, Type t)
{
MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
foreach (var item in t.GetProperties())
{
Type _t = item.PropertyType;
if (_t.Namespace != "System")
{
DefineProperties(tBuilder, _t);
}
else
{
FieldBuilder customerNameBldr = tBuilder.DefineField("_" + item.Name, item.PropertyType, FieldAttributes.Private);
PropertyBuilder custNamePropBldr = tBuilder.DefineProperty(item.Name, System.Reflection.PropertyAttributes.HasDefault, item.PropertyType, null);
MethodBuilder custNameGetPropMthdBldr = tBuilder.DefineMethod("get_" + item.Name, getSetAttr, item.PropertyType, Type.EmptyTypes);
ILGenerator custNameGetIL = custNameGetPropMthdBldr.GetILGenerator();
custNameGetIL.Emit(OpCodes.Ldarg_0);
custNameGetIL.Emit(OpCodes.Ldfld, customerNameBldr);
custNameGetIL.Emit(OpCodes.Ret);
MethodBuilder custNameSetPropMthdBldr = tBuilder.DefineMethod("set_" + item.Name, getSetAttr,null, new Type[] { item.PropertyType });
ILGenerator custNameSetIL = custNameSetPropMthdBldr.GetILGenerator();
custNameSetIL.Emit(OpCodes.Ldarg_0);
custNameSetIL.Emit(OpCodes.Ldarg_1);
custNameSetIL.Emit(OpCodes.Stfld, customerNameBldr);
custNameSetIL.Emit(OpCodes.Ret);
custNamePropBldr.SetGetMethod(custNameGetPropMthdBldr);
custNamePropBldr.SetSetMethod(custNameSetPropMthdBldr);
}
}
}
}
Sure, you can write it more generic, but it should show you how you could create a "dynamic type" in C#.
Additionally I copy the data of the old structure into the new one and change the datasource to the new list of my dynamic type. Thats all ;)
If you have further questions, i'll try to help you.
The result:
I have a datagridview that I am binding to a class. I add to the class but the datagridview is not updating.
My bind:
ScannedChecks = new ScannedChecks();
ScannedChecks.AddCheck(DateTime.Now, "22222", "checknumdd", "routingdd", _checkData, 4);
dataGridView1.DataSource = ScannedChecks;
I went ahead and did the AddCheck to see if it was reaching the datagridview and it isn't... The class is being updated though.
My class:
namespace SSS.Ckentry
{
public class ScannedChecks : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ScannedChecks()
{
ScannedChecksCollection = new ObservableCollection<ScannedCheck>();
}
public void AddCheck(DateTime checkDate, string accountNumber, string checkNumber, string bankRoutingNumber, string bankAccountNumber, decimal checkAmount)
{
var scc = new ScannedCheck
{
CheckDate = checkDate,
AccountNumber = accountNumber,
CheckNumber = checkNumber,
BankRoutingNumber = bankRoutingNumber,
BankAccountNumber = bankAccountNumber,
CheckAmount = checkAmount,
};
ScannedChecksCollection.Add(scc);
}
public ObservableCollection<ScannedCheck> ScannedChecksCollection { get; set; }
public class ScannedCheck
{
public DateTime CheckDate { get; set; }
public string AccountNumber { get; set; }
public string CheckNumber { get; set; }
public string BankRoutingNumber { get; set; }
public string BankAccountNumber { get; set; }
public decimal CheckAmount { get; set; }
}
}
}
Can anyone tell me what I am doing wrong?
Thanks much!
If you ever replace the ScannedChecksCollection with a new ScannedChecksCollection, the property setter should fire the PropertyChanged exent.
private ObservableCollection<ScannedCheck> scannedChecksCollection;
public ObservableCollection<ScannedCheck> ScannedChecksCollection {
get
{
return scannedChecksCollection;
}
set
{
if (value != scannedChecksCollection)
{
value = scannedChecksCollection;
NotifyPropertyChanged("ScannedChecksCollection");
}
}
}
private void NotifyPropertyChanged(string propName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
If checks are modifiable, ScannedCheck should implement INotifyPropertyChanged
Shouldn't you be doing
dataGridView1.DataSource = ScannedChecks.ScannedChecksCollection;
I have a few types that make up a hierarchy like this:
+ Image0.Name
Effect0.Name
Effect1.Name
Effect2.Name
Layer0.Name
Layer1.Name
Layer2.Name
...
+ Image1.Name
Effect0.Name
Effect1.Name
Effect2.Name
Layer0.Name
Layer1.Name
Layer2.Name
...
+ Image2.Name
Effect0.Name
Effect1.Name
Effect2.Name
Layer0.Name
Layer1.Name
Layer2.Name
...
But I can't get my head around the data binding. Here is the code for the types:
public class Image
{
public string Name { get; set; }
public IEnumerable<Effect> Effects { get; set; }
public IEnumerable<Layer> Layers { get; set; }
}
public class Effect
{
public string Name { get; set; }
public Effect ( string name )
{
this.Name = name;
}
}
public class Layer
{
public string Name { get; set; }
public Layer ( string name )
{
this.Name = name;
}
}
public class EditorView : INotifyPropertyChanged
{
IEnumerable<Node> images;
public IEnumerable<Node> Images
{
get { return images; }
set
{
this.images = value;
this.RaisePropertyChanged ( "Images" );
}
}
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged ( string propertyName )
{
var handler = this.PropertyChanged;
if ( handler != null )
handler ( this, new PropertyChangedEventArgs ( propertyName ) );
}
}
Additionally these types (Effect, Layer) has a unique icon per type, if you can also show how to bind this, that would help me a lot in understanding it all.
This is how I normally do it, create a base class for the child items and then create I property that returns all the child Items
public class Image
{
public string Name { get; set;}
public IEnumerable<Effect> Effects { get; set; }
public IEnumerable<Layer> Layers { get; set; }
public IEnumerable<Node> Nodes { get { return ((IEnumerable<Node>)Layers).Union((IEnumerable<Node>)Effects); } }
}
public class Effect : Node
{
public Effect(string name)
{
this.Name = name;
}
}
public class Layer : Node
{
public Layer(string name) { this.Name = name; }
}
public class Node
{
public string Name { get; set; }
public Image Icon { get; set; }
}
You should be able to set the Image Property (url of image but you can change the property type) of the respective Effects and Layers and then I've already wired it up, this should work
<TreeView Height="221" HorizontalAlignment="Left" Margin="12,12,0,0" Name="treeView1"
VerticalAlignment="Top" Width="479" ItemsSource="{Binding Images}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Nodes}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"></TextBlock>
<Image Source="{Binding Icon}"></Image>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
EDIT
And then set the TreeView Item's DataContext to your ViewModel, just as an example I did this in the code behind:
Image img = new Image();
Effect effect = new Effect("Effect1");
Layer layer = new Layer("Layer1");
img.Name = "Image1";
List<Effect> effects = new List<Effect>();
effects.Add(effect);
img.Effects = effects;
List<Layer> layers = new List<Layer>();
layers.Add(layer);
img.Layers = layers;
List<WpfApplication1.Image> Images = new List<Image>();
Images.Add(img);
EditorView ev = new EditorView();
ev.Images = Images;
treeView1.DataContext = ev;
EDIT2: Pasted complete code (without using statements):
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new EditorView();
}
}
public class Image
{
public string Name { get; set;}
public IEnumerable<Effect> Effects { get; set; }
public IEnumerable<Layer> Layers { get; set; }
public IEnumerable<Node> Nodes { get { return ((IEnumerable<Node>)Layers).Union((IEnumerable<Node>)Effects); } }
}
public class Effect : Node
{
public Effect(string name)
{
this.Name = name;
}
}
public class Layer : Node
{
public Layer(string name) { this.Name = name; }
}
public class Node
{
public string Name { get; set; }
public string Icon { get; set; }
}
public class EditorView : INotifyPropertyChanged
{
public EditorView()
{
Image img = new Image();
WpfApplication1.Effect effect = new WpfApplication1.Effect("Effect1");
WpfApplication1.Layer layer = new Layer("Layer1");
img.Name = "Image1";
List<Effect> effects = new List<WpfApplication1.Effect>();
effects.Add(effect);
img.Effects = effects;
List<Layer> layers = new List<Layer>();
layers.Add(layer);
img.Layers = layers;
List<WpfApplication1.Image> Images = new List<Image>();
Images.Add(img);
this.Images = Images;
}
IEnumerable<Image> images;
public IEnumerable<Image> Images
{
get
{
return images;
}
set { this.images = value; this.RaisePropertyChanged("Images");
}
} public event
PropertyChangedEventHandler
PropertyChanged;
void RaisePropertyChanged(string propertyName)
{ var handler = this.PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
You need to implement the HierarchicalDataTemplate. For sample of it look at the last of this article - http://msdn.microsoft.com/en-us/library/ms742521.aspx and this one - http://blogs.msdn.com/b/chkoenig/archive/2008/05/24/hierarchical-databinding-in-wpf.aspx