I want to show user's Folder
(C:\Food\BBQ\Recipe\Recipefile.txt)
enter image description here
enter image description here
like that
but result is ...
enter image description here
I make a project MVVM patteron wpf
Using ViewModel.cs and View
with HierarchicalDataTemplate
this is my codes
window1.xaml
<Window.Resources>
<ObjectDataProvider x:Key="ObjectDataProviderKey">
<ObjectDataProvider.ObjectInstance>
<vm:FolderViewModel FullPath="C:\Food"/>
</ObjectDataProvider.ObjectInstance>
</ObjectDataProvider>
<HierarchicalDataTemplate
DataType="{x:Type vm:FolderViewModel}"
ItemsSource="{Binding Path=SubFolderCollection}">
<TextBlock Text="{Binding Path=Name}" />
</HierarchicalDataTemplate>
<TreeView Name="folderTreeView" Grid.ColumnSpan="2" Grid.Row="2">
<TreeViewItem
Header="Favorit"
ItemsSource="{Binding Source={StaticResource ObjectDataProviderKey}, Path=SubFolderCollection}" />
</TreeView>
and
viewModel
FolderViewModel.cs
namespace TEST.ViewModels.TreeView
{
public class FolderViewModel : INotifyPropertyChanging
{
namespace TEST.ViewModels.TreeView
{
public class FolderViewModel : INotifyPropertyChanging
{
#region Field
private DirectoryInfo directoryInfo;
private ObservableCollection<FolderViewModel> subFolderCollection;
private ObservableCollection<FileInfo> fileInfoCollection;
#endregion
#region - FullPath
public string FullPath
{
get
{
return directoryInfo.FullName;
}
set
{
if (Directory.Exists(value))
{
directoryInfo = new DirectoryInfo(value);
}
else
{
throw new ArgumentException("No exist.", "FullPath");
}
}
}
#endregion
#region - Name
private string _Name = string.Empty;
public event PropertyChangingEventHandler PropertyChanging;
public string Name
{
get
{
_Name = directoryInfo.Name;
return _Name;
}
set
{
_Name = value;
OnpropertyChanaged("Name");
}
}
private void OnpropertyChanaged(string v)
{
throw new NotImplementedException();
}
#endregion
#region - SubFolderCollection
public ObservableCollection<FolderViewModel> SubFolderCollection
{
get
{
if (subFolderCollection == null)
{
subFolderCollection = new ObservableCollection<FolderViewModel>();
DirectoryInfo[] directoryInfoArray = directoryInfo.GetDirectories();
//DirectoryInfo[] directoryInfoArray = (DirectoryInfo[])this.directoryInfo.GetFileSystemInfos();
for (int i = 0; i < directoryInfoArray.Length; i++)
{
FolderViewModel folder = new FolderViewModel();
FullPath = directoryInfoArray[i].FullName;
this.subFolderCollection.Add(folder);
}
}
return subFolderCollection;
}
}
#endregion
#region FileInfoCollection
public ObservableCollection<FileInfo> FileInfoCollection
{
get
{
if (this.fileInfoCollection == null)
{
this.fileInfoCollection = new ObservableCollection<FileInfo>();
FileInfo[] fileInfoArray = this.directoryInfo.GetFiles();
for (int i = 0; i < fileInfoArray.Length; i++)
{
this.fileInfoCollection.Add(fileInfoArray[i]);
}
}
return this.fileInfoCollection;
}
}
#endregion
#region - Folder()
public FolderViewModel()
{
FullPath = #"C:\Food\";
}
#endregion
}
}
what should i do ??
If I understood you correctly. To Achieive the way you wanted you can do something like this.
MainWindow.xaml
<TreeView ItemsSource="{Binding TreeModel}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Child}">
<Grid>
<Label Content="{Binding Name}"/>
</Grid>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
And Then your ViewModel you can create a collection which goes through your folders and add folder and file name
MainViewModel.cs
public ObservableCollection<DirModel> TreeModel { get; set; }
public MainViewModel()
{
TreeModel = new ObservableCollection<DirModel>();
Load();
}
void Load()
{
var root = "C:\\TEST";
foreach (var dirInfo in new DirectoryInfo(root).GetDirectories())
{
var dir = new DirModel() { Name = dirInfo.Name };
foreach (var sb in dirInfo.GetDirectories())
{
var sDir = new ChildDirModel() { Name = sb.Name };
var sFile = new FileModel() { Name = sb.GetFiles().First().Name };
sDir.Child.Add(sFile);
dir.Child.Add(sDir);
}
TreeModel.Add(dir);
}
}
Finally create a Model class which represent your structure
public class DirModel
{
public string Name { get; set; }
public ObservableCollection<ChildDirModel> Child { get; set; }
public DirModel()
{
Child = new ObservableCollection<ChildDirModel>();
}
}
public class ChildDirModel
{
public string Name { get; set; }
public ObservableCollection<FileModel> Child { get; set; }
public ChildDirModel()
{
Child = new ObservableCollection<FileModel>();
}
}
public class FileModel
{
public string Name { get; set; }
}
Your application will look like this
Related
I have a DispatcherTimer in my ViewModel that i can see firing every interval, but the view is not being updated?
the feed data comes from a xml url and i am trying to refresh the form every x seconds. Maybe more or less lables / differnt status
heres the code snippets:
ViewModel.cs
public class Nodes
{
public string name { get; set; }
public string id { get; set; }
public string status { get; set; }
public string last { get; set; }
public int level { get; set; }
public string parent { get; set; }
}
public ObservableCollection<CI> CIs
{
get;
set;
}
DispatcherTimer LogTimer;
public void LoadCIs()
{
ObservableCollection<CI> cis = new ObservableCollection<CI>();
LogTimer = new DispatcherTimer();
LogTimer.Interval = TimeSpan.FromMilliseconds(10000);
LogTimer.Tick += (s, e) =>
{
//pull node list
List<Nodes> SortedList = PopulateNodes();
foreach (Nodes Node in SortedList)
{
//create labels for all top level nodes
if (Node.level == 3)
{
cis.Add(new CI { NodeName = Node.name, NodeStatus = Node.status });
}
}
CIs = cis;
};
LogTimer.Start();
}
Model.cs
public class CI : INotifyPropertyChanged {
private string nodeName;
private string nodeStatus;
public string NodeName {
get {
return nodeName;
}
set {
if (nodeName != value) {
nodeName = value;
RaisePropertyChanged("NodeName");
}
}
}
public string NodeStatus
{
get
{
return nodeStatus;
}
set
{
if (nodeStatus != value)
{
nodeStatus = value;
RaisePropertyChanged("NodeStatus");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property) {
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
view.xaml
<Grid>
<ItemsControl ItemsSource = "{Binding Path = CIs}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Label Content = "{Binding Path = NodeName, Mode = OneWay}"
Background = "{Binding Path = NodeStatus, Mode = OneWay}"
Foreground="White"
FontFamily="Arial Black"
HorizontalContentAlignment="Center"
BorderBrush="Black"
BorderThickness="1,1,1,1"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
how the form should look without timer enabled / commented out:
with the timer code enabled nothing is added to grid:
Thanks for looking
The problem:
You are changing the Collection CIs but do not notify it is changed. ObservableCollections report their changes but you're overwriting it, it will not report that.
Option 1:
Because you use an ObservableCollection you can add it to the bound collection directly and it will notify the UI automatically.
So instead of:
cis.Add(new CI { NodeName = Node.name, NodeStatus = Node.status });
Do this:
CIs.Add(new CI { NodeName = Node.name, NodeStatus = Node.status });
if you do this you have to initialize CIs first:
public ObservableCollection<CI> CIs
{
get;
set;
} = new ObservableCollection<CI>(); // < initialize it
Option 2:
Add the INotifyPropertyChanged interface to the Nodes class and notify like this:
this.PropertyChanged?.Invoke( this, new PropertyChangedEventArgs( nameof( this.CIs ) ) );
in the setter of CIs
I'm having a problem getting the content to display in my program, and I suspect I messed up something relating to the DataContext. The controls I am using come from an extension called Syncfusion (to display graphs) but it could be any other control displaying these items.
MainWindow.xaml.cs:
public MainWindow()
{
InitializeComponent();
ViewModel _viewModel = new ViewModel();
DataContext = _viewModel;
}
ViewModel.cs
public class ViewModel
{
public ObservableCollection<TotalData> TotalDataColl { get; set; }
public ViewModel()
{
TotalDataColl = new ObservableCollection<TotalData>();
var vm = new ChartViewModel
{
Series = new ObservableCollection<SeriesViewModel>
{
new SeriesViewModel{type="Lemons", Items = new ObservableCollection<ItemViewModel>{new ItemViewModel{source = "January", value = 25}, new ItemViewModel{source = "February", value = 35}}},
new SeriesViewModel{type="Oranges",Items = new ObservableCollection<ItemViewModel>{new ItemViewModel{source = "January", value = 22}, new ItemViewModel{source = "February", value = 36}}}
}
};
}
}
MainWindow.xaml
<Grid>
<local:ExtendedChart ItemsSource="{Binding Series}" Margin="0,-38,0,97">
<local:ExtendedChart.ItemTemplate>
<DataTemplate>
<chart:ColumnSeries ItemsSource="{Binding Items}" DependentValuePath="value" IndependentValuePath="source" Title="{Binding type}" />
</DataTemplate>
</local:ExtendedChart.ItemTemplate>
</local:ExtendedChart>
</Grid>
DataClass.cs
public class ChartViewModel : ObservableObject
{
private ObservableCollection<SeriesViewModel> _series;
public ObservableCollection<SeriesViewModel> Series
{
get
{
return _series;
}
set
{
_series = value;
OnPropertyChanged("Series");
}
}
}
public class SeriesViewModel : ObservableObject
{
private ObservableCollection<ItemViewModel> items;
private string _type;
public string type { get { return _type; } set { _type = value; OnPropertyChanged("_type"); } }
public ObservableCollection<ItemViewModel> Items { get { return items; } set { items = value; OnPropertyChanged("Items"); } }
}
public class ItemViewModel : ObservableObject
{
private string _source;
private double _value;
public string Source { get { return _source;} set { _source = value; OnPropertyChanged("Source"); } }
public double Value { get { return _value; } set { _value = value;OnPropertyChanged("Value"); } }
}
ViewModel
public class ViewModel
{
public ObservableCollection<TotalData> TotalDataColl { get; set; }
public ChartViewModel ChartSeriesViewModel { get; set; }
public ViewModel()
{
TotalDataColl = new ObservableCollection<TotalData>();
ChartSeriesViewModel = new ChartViewModel
{
Series = new ObservableCollection<SeriesViewModel>
{
new SeriesViewModel{type="Lemons", Items = new ObservableCollection<ItemViewModel>{new ItemViewModel{source = "January", value = 25}, new ItemViewModel{source = "February", value = 35}}},
new SeriesViewModel{type="Oranges",Items = new ObservableCollection<ItemViewModel>{new ItemViewModel{source = "January", value = 22}, new ItemViewModel{source = "February", value = 36}}}
}
};
}
}
MainWindow.Xaml
<Grid>
<local:ExtendedChart ItemsSource="{Binding ChartSeriesViewModel.Series}" Margin="0,-38,0,97">
<local:ExtendedChart.ItemTemplate>
<DataTemplate>
<chart:ColumnSeries ItemsSource="{Binding Items}" DependentValuePath="value" IndependentValuePath="source" Title="{Binding type}" />
</DataTemplate>
</local:ExtendedChart.ItemTemplate>
</local:ExtendedChart>
</Grid>
I want to create treeView as following with full respect of Wpf MVVM:
Root //Level 0
B1- Child1 // Level 1
B1-1- Child1-1 // Level 2
B1-2- Child1-2
B1-3- ...
B2- Child2 // Level1
B2-1 Child2-1 // Level 2
B2-2 Child2-2
B2-3 ...
As you can see, I have TreeView with 3 Level.
Level 0: Root (Always Fix)
Level 1: Two child (Always Fix too)
Level 2: Dynamic Child and they are created from two different class
So My question is how Can I create different child in Level 2 for every node in Level 1.
I used the code below but I have always the same children under parents in Level 1.
I have looked at many of the solution proposed on this site as well as on the web... but just cant figure out how to do it...
My attempt:
public class MyViewModel
{
//private ReadOnlyCollection<AttributesMapDocViewModel> _attributeMapDoc;
public object _document;
#region Methodes
private List<Level0ViewModel> _myDoc;
public List<Level0ViewModel> MyDoc
{
get { return _myDoc; }
set
{
_myDoc = value;
}
}
#endregion
#region Constructeur
public MyViewModel()
{
MyDoc = new List<Level0ViewModel>()
{
new Level0ViewModel("Root",_document), //_document conatins data from xml file (code not shown)
};
}
#endregion
}
public class Level0ViewModel : ViewModelBase
{
private List<Level1ViewModel> _childLevel1;
public Level0ViewModel(string name, object myObj)
{
ChildLeve0Name = name;
ChildLevel1 = new List<Level1ViewModel>()
{
new Level1ViewModel("Child1",myObj),
new Level1ViewModel("Child2",myObj)
};
}
public List<Level1ViewModel> ChildLevel1
{
get
{
return _childLevel1;
}
set
{
SetProperty(ref _childLevel1, value, () => ChildLevel1);
}
}
public string ChildLeve0Name { get; set; }
}
public class Level1ViewModel : ViewModelBase
{
private ObservableCollection<Level2SecondTypeViewModel> _childLevel2SecondType;
public ObservableCollection<Level2SecondTypeViewModel> ChildLevel2SecondType
{
get { return _childLevel2SecondType; }
set
{
if (_childLevel2SecondType != value)
{
SetProperty(ref _childLevel2SecondType, value, () => ChildLevel2SecondType);
}
}
}
private ObservableCollection<Level2FirstTypeViewModel> _childLevel2FirstType;
public ObservableCollection<Level2FirstTypeViewModel> ChildLevel2FirstType
{
get { return _childLevel2FirstType; }
set
{
if (_childLevel2FirstType != value)
{
SetProperty(ref _childLevel2FirstType, value, () => ChildLevel2FirstType);
}
}
}
public Level1ViewModel(string name, object mapAtt)
{
ChildLevel1Name = name;
ChildLevel2FirstType = new ObservableCollection<Level2FirstTypeViewModel>();
foreach (FirstType myFirstType in mapAtt.FirstTypes)
{
ChildLevel2FirstType.Add(new Level2FirstTypeViewModel(myFirstType));
}
ChildLevel2SecondType = new ObservableCollection<Level2SecondTypeViewModel>();
foreach (SecondType mySecondType in mapAtt.SecondTypes)
{
ChildLevel2SecondType.Add(new Level2SecondTypeViewModel(mySecondType));
}
}
public string ChildLevel1Name
{
get;
set;
}
}
public class Level2FirstTypeViewModel : ViewModelBase
{
public Level2FirstTypeViewModel(FirstType fType)
{
FirstTypeName = fType.name;
}
public string FirstTypeName
{
get;
set;
}
}
public class Level2SecondTypeViewModel
{
public Level2SecondTypeViewModel(SecondType sType)
{
SecondTypeName = sType.name;
}
public string SecondTypeName
{
get;
set;
}
}
<TreeView ItemsSource="{Binding MyDoc}" >
<TreeView.Resources>
<HierarchicalDataTemplate ItemsSource="{Binding MapAttDef}" DataType="{x:Type local:Level0ViewModel}">
<Label Content="{Binding ChildLeve0Name}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding ChildLevel2SecondType}" DataType="{x:Type local:Level1ViewModel}">
<Label Content="{Binding ChildLevel1Name}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Level2SecondTypeViewModel}">
<Label Content="{Binding FirstTypeName}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Level2FirstTypeViewModel}">
<Label Content="{Binding SecondTypeName}"/>
</HierarchicalDataTemplate>
</TreeView.Resources>
My Attempt give me something like this ( that isn't what I want at all !!):
Root //Level 0
B1- Child1 // Level 1
B1-1- Child1-1 // Level 2
B1-2- Child1-2
B1-3- ...
B2- Child2 // Level1
B2-1 Child1-1 // Level 2
B2-2 Child1-2
B2-3 ...
Throw away these properties: Level1ViewModel.ChildLevel2FirstType and Level1ViewModel.ChildLevel2SecondType.
You need single collection for children. The item type of this collection depends on inheritance hierarchy and should be nearest common ancestor (worst case is object), that is, like this:
public Level1ViewModel
{
public ObservableCollection<object> ChildLevel2
{
// ...
}
// ...
}
Markup with these changes will look like this:
<!-- Level 1 -->
<HierarchicalDataTemplate DataType="{x:Type local:Level1ViewModel}" ItemsSource="{Binding ChildLevel2}">
<Label Content="{Binding ChildLevel1Name}"/>
</HierarchicalDataTemplate>
<!-- Level 1 -->
<DataTemplate DataType="{x:Type local:Level2FirstTypeViewModel}">
<Label Content="{Binding SecondTypeName}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Level2SecondTypeViewModel}">
<Label Content="{Binding FirstTypeName}"/>
</DataTemplate>
Note, that HierarchicalDataTemplate must be used for hierarchical data sources only. For non-hierarchical ones (like Level2FirstTypeViewModel and
Level2SecondTypeViewModel) use regular DataTemplate.
public class BaseTreeViewItem<TSource, TParent> : ViewModelBase
{
// add static field to hold selected Item
public static BaseTreeViewItem<TSource, TParent> SelectedChild { get; set; }
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
if (_isSelected == value) return;
if (SelectedChild != null)
{
SelectedChild.IsSelected = false;
SelectedChild = null;
}
_isSelected = value;
if (_isSelected)
SelectedChild = this;
// NotifyPropertyChanged
}
}
public TParent Parent
{
get;
private set;
}
public BaseTreeViewItem(string name, TSource myObj, TParent parent)
{
ChildName = name;
DataSource = myObj;
Parent = parent;
}
public string ChildName { get; set; }
public TSource DataSource { get; set; }
public override string ToString()
{
return ChildName;
}
}
// Node
public class MyTreeViewItem<TChild, TSource, TParent> : BaseTreeViewItem<TSource, TParent>, IDeleteItem
where TParent : IDeleteItem
where TChild : class
{
public ObservableCollection<TChild> Children { get; set; }
public MyTreeViewItem(string name, TSource myObj, TParent parent)
:base(name,myObj, parent)
{
Children = new ObservableCollection<TChild>();
}
protected virtual void InitChild()
{
}
public void DeleteItem(object myTreeViewItem)
{
Children.Remove(myTreeViewItem as TChild);
}
public static void DeleteSelectedItem()
{
if (SelectedChild != null && SelectedChild.Parent != null)
{
SelectedChild.Parent.DeleteItem(SelectedChild);
}
}
}
public class Level0ViewModel : MyTreeViewItem<Level1ViewModel, XmlDocument, IDeleteItem>
{
protected override sealed void InitChild()
{
base.InitChild();
Children.Add(new Level1ViewModel("Child1", new Level1Src(), this));
Children.Add(new Level1ViewModel("Child2", new Level1Src(), this));
}
public Level0ViewModel(string name, XmlDocument myObj) :
base(name, myObj,null)
{
InitChild();
}
}
public class Level1ViewModel : MyTreeViewItem<Level2TypeViewModel, Level1Src, Level0ViewModel>
{
public Level1ViewModel(string name, Level1Src myObj, Level0ViewModel parent)
: base(name, myObj, parent)
{
InitChild();
}
protected override sealed void InitChild()
{
base.InitChild();
foreach (FirstType myFirstType in DataSource.FirstTypes)
{
Children.Add(new Level2TypeViewModel(myFirstType, this));
}
foreach (SecondType mySecondType in DataSource.SecondTypes)
{
Children.Add(new Level2TypeViewModel(mySecondType, this));
}
}
// Use linq if tou want child by type
public IEnumerable<Level2TypeViewModel> ChildType1
{
get
{
return Children.Where(item => item.DataSource is FirstType);
}
}
public IEnumerable<Level2TypeViewModel> ChildType2
{
get
{
return Children.Where(item => item.DataSource is SecondType);
}
}
}
public class LevelType
{
public string Name;
}
public class FirstType : LevelType
{
}
public class SecondType : LevelType
{
}
public class Level2TypeViewModel : BaseTreeViewItem<LevelType, Level1ViewModel>
{
public Level2TypeViewModel(LevelType sType, Level1ViewModel parent)
: base(sType.Name, sType, parent)
{
}
}
My Data Source is
public Level0ViewModel TreeModel
{
get
{
return new Level0ViewModel("Root", new XmlDocument());
}
}
For TreeView DataTemplate use
<TreeView DataContext="{Binding ElementName=UI, Path=TreeModel}">
<TreeView.Items>
<TreeViewItem Header="{Binding ChildName}" ItemsSource="{Binding Path=Children}" >
<TreeViewItem.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<Label Content="{Binding ChildName}"></Label>
</HierarchicalDataTemplate>
</TreeViewItem.ItemTemplate>
</TreeViewItem>
</TreeView.Items>
</TreeView>
The First treeviewItem is to show root node. if you do not want to use it
You can change data source to
public MyTreeViewItem<Level0ViewModel,object> TreeModel
{
get
{
MyTreeViewItem<Level0ViewModel,object> src = new MyTreeViewItem<Level0ViewModel, object>("Root", null);
src.Children.Add(new Level0ViewModel("toto", new XmlDocument()));
return src;
}
}
And DataTemplate to
<TreeView DataContext="{Binding ElementName=UI, Path=TreeModel}" ItemsSource="{Binding Children}">
<TreeViewItem.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<Label Content="{Binding ChildName}"></Label>
</HierarchicalDataTemplate>
</TreeViewItem.ItemTemplate>
</TreeView>
sample way
public class TreeViewItemViewModel : ViewModelBase
{
// add static field to hold selected Item
public static TreeViewItemViewModel SelectedChild { get; set; }
public TreeViewItemViewModel Parent{ get; private set;}
public string ChildName { get; set; }
public object DataSource { get; set; }
private readonly ObservableCollection<TreeViewItemViewModel> _children;
public ObservableCollection<TreeViewItemViewModel> Children
{
get
{
return _children;
}
}
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
if (_isSelected == value) return;
if (SelectedChild != null)
{
SelectedChild.IsSelected = false;
SelectedChild = null;
}
_isSelected = value;
if (_isSelected)
SelectedChild = this;
// NotifyPropertyChanged
}
}
public TreeViewItemViewModel(string name, object myObj, TreeViewItemViewModel parent)
{
ChildName = name;
DataSource = myObj;
Parent = parent;
_children = new ObservableCollection<TreeViewItemViewModel>();
}
public override string ToString()
{
return ChildName;
}
protected virtual void InitChild()
{
}
public void DeleteItem(TreeViewItemViewModel myTreeViewItem)
{
Children.Remove(myTreeViewItem);
}
public static void DeleteSelectedItem()
{
if (SelectedChild != null && SelectedChild.Parent != null)
{
SelectedChild.Parent.DeleteItem(SelectedChild);
}
}
}
If u have one selected Item in your view model u can use this approach
public class TreeViewItemViewModel : ViewModelBase
{
// add static field to hold selected Item
public static TreeViewItemViewModel SelectedChild { get; set; }
public TreeViewItemViewModel Parent{ get; private set;}
public string ChildName { get; set; }
public object DataSource { get; set; }
private readonly ObservableCollection<TreeViewItemViewModel> _children;
public ObservableCollection<TreeViewItemViewModel> Children
{
get
{
return _children;
}
}
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
if (_isSelected == value) return;
if (SelectedChild != null)
{
SelectedChild.IsSelected = false;
SelectedChild = null;
}
_isSelected = value;
if (_isSelected)
SelectedChild = this;
// NotifyPropertyChanged
}
}
public TreeViewItemViewModel(string name, object myObj, TreeViewItemViewModel parent)
{
ChildName = name;
DataSource = myObj;
Parent = parent;
_children = new ObservableCollection<TreeViewItemViewModel>();
}
public override string ToString()
{
return ChildName;
}
protected virtual void InitChild()
{
}
public void DeleteItem(TreeViewItemViewModel myTreeViewItem)
{
Children.Remove(myTreeViewItem);
}
public static void DeleteSelectedItem()
{
if (SelectedChild != null && SelectedChild.Parent != null)
{
SelectedChild.Parent.DeleteItem(SelectedChild);
}
}
}
I have a ComboBox in my XAML. It is populated with the following code:
PopulateColors.cs
public class PopulateColors
{
public ObservableCollection<ItemsColors> itemsColors { get; set; }
public PopulateColors()
{
this.itemsColors = new ObservableCollection<ItemsColors>();
this.itemsColors.Add(new ItemsColors{ ItemColumn = "Blue", IdItemColumn = 0 });
this.itemsColors.Add(new ItemsColors{ ItemColumn = "Red", IdItemColumn = 1 });
this.itemsColors.Add(new ItemsColors{ ItemColumn = "Pink", IdItemColumn = 2 });
}
}
public class ItemsColors
{
public string ItemColumn { get; set; }
public int IdItemColumn { get; set; }
}
pagedemo.xaml.cs:
ClothingViewModel ClothVM = null;
public pagedemo()
{
this.comboColors.DataContext = new PopulateColors();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
ClothVM = new ClothingViewModel();
ClothVM = ClothVM.GetData(1);
this.DataContext = ClothVM ;
navigationHelper.OnNavigatedTo(e);
}
private void SaveButton_Click(object sender, RoutedEventArgs e)
{
string result = ClothVM .Save( ClothVM );
if (result.Contains("OK"))
{
//to do
}
}
pagedemo.xaml (XAML design)
<TextBox x:Name="txtJersey"
Text="{Binding Jersey, Mode=TwoWay}"/>
<ComboBox Name="comboColors"
ItemsSource="{Binding itemsColors}"
DisplayMemberPath="ItemColumn"
SelectedValuePath="ItemColumn"/>
Ok. items are display fine in ComboBox.
QUESTION:
I need to save the selected color in a table of the database, using MVVM pattern. But how? I have this code. But I do not know how to link it with the ComboBox:
Model: Clothing.cs
Public class Clothing
{
public string Color { get; set; }
public string Jersey { get; set; }
}
ViewModel: ClothingViewModel.cs
public class ClothingViewModel : ViewModelBase
{
public string Save (ClothingViewModel cloth)
{
string result = string.Empty;
using (var db = new SQLite.SQLiteConnection(App.DBPath))
{
string change = string.Empty;
try
{
var existing = (db.Table<Clothing>().Where(
c => c.id == 1)).SingleOrDefault();
if (existing!= null)
{
existing.Color = cloth.Color;
existing.Jersey = cloth.Jersey;
int success = db.Update(existing);
}
}
catch
{ }
}
}
private int id = 1;
public int ID
{
get
{ return id; }
set
{
if (id == value)
{ return; }
id= value;
RaisePropertyChanged("ID");
}
}
private string color = string.Empty;
public string Color
{
get
{ return color; }
set
{
if (color == value)
{ return; }
color = value;
isDirty = true;
RaisePropertyChanged("Color");
}
}
private string jersey = string.Empty;
public string Jersey
{
get
{ return jersey; }
set
{
if (jersey == value)
{ return; }
jersey = value;
isDirty = true;
RaisePropertyChanged("Jersey");
}
}
}
Actually, there are plenty of options. Let's demonstrate just a few of them.
1. Use Binding with RelativeSource to find Ancestor with appropriate DataContext
XAML-code:
<!-- Please use Page instead of Window. -->
<Window>
<StackPanel>
<TextBox x:Name="txtJersey"
Text="{Binding Jersey, Mode=TwoWay}"/>
<!-- Use {x:Type Page} instead of {x:Type Window}. -->
<ComboBox Name="comboColors" ItemsSource="{Binding itemsColors}"
DisplayMemberPath="ItemColumn"
SelectedValue="{Binding
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},
Path=DataContext.Color}"
SelectedValuePath="ItemColumn" />
</StackPanel>
</Window>
2. Use Binding with ElementName to the UI-element with appropriate DataContext
XAML-code:
<StackPanel>
<TextBox x:Name="txtJersey"
Text="{Binding Jersey, Mode=TwoWay}"/>
<ComboBox Name="comboColors" ItemsSource="{Binding itemsColors}"
DisplayMemberPath="ItemColumn"
SelectedValue="{Binding
ElementName=txtJersey,
Path=DataContext.Color}"
SelectedValuePath="ItemColumn" />
</StackPanel>
3. Use "one" ViewModel that contains another ViewModel
public class ClothingViewModel : ViewModelBase
{
private readonly PopulateColors colors = new PopulateColors();
public PopulateColors Colors
{
get { return this.colors; }
}
...
}
Page:
// This is a Page (Window in case of using WPF).
public class ClothingWindow
{
public ClothingWindow()
{
InitializeComponent();
// Note: no need to set the DataContext for the ComboBox.
DataContext = new ClothingViewModel();
}
}
XAML-code:
<StackPanel>
<TextBox Text="{Binding Jersey, Mode=TwoWay}"/>
<ComboBox ItemsSource="{Binding Colors.itemsColors}"
DisplayMemberPath="ItemColumn"
SelectedValue="{Binding Color}"
SelectedValuePath="ItemColumn" />
</StackPanel>
References
Data Binding (WPF), MSDN.
Data Binding in WPF, John Papa, MSDN Magazine.
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