I'm trying to set property value from another class to XAML UI element property .
I have XAML and static class called "Config".
In Config class I have public static class Theme.
In the class Theme I have property primaryColor.
So I need to set primaryColor to UI element in XAML.
I tried x:Static, but it doesnt work for me, cause the field in the Theme class is not static.
XAML:
<StackLayout BackgroundColor={x:Static config:Config.CurrentTheme.primaryColor}></StackLayout>
Config.cs:
public static class Config
{
public static Theme CurrentTheme { get; set; }
}
Theme.cs:
public class Theme
{
public Color primaryColor { get; set; } = Color.FromHex("#1D1E1F");
public Color secondaryColor { get; set; } = Color.FromHex("#252625");
public Color grayColor { get; set; } = Color.FromHex("#2F2F2F");
public Color lightGrayColor { get; set; } = Color.FromHex("#626261");
public Color goldColor { get; set; } = Color.FromHex("#CAA440");
public Color lightGreenColor { get; set; } = Color.FromHex("#28A745");
public Color darkRedColor { get; set; } = Color.FromHex("#F0373A");
}
Adding Static to Theme.cs , then can be used in Xaml :
public static class Theme
{
public static Color primaryColor { get; set; } = Color.FromHex("#1D1E1F");
public static Color secondaryColor { get; set; } = Color.FromHex("#252625");
public static Color grayColor { get; set; } = Color.FromHex("#2F2F2F");
public static Color lightGrayColor { get; set; } = Color.FromHex("#626261");
public static Color goldColor { get; set; } = Color.FromHex("#CAA440");
public static Color lightGreenColor { get; set; } = Color.FromHex("#28A745");
public static Color darkRedColor { get; set; } = Color.FromHex("#F0373A");
}
Xaml :
<StackLayout BackgroundColor="{x:Static local:Theme.grayColor}"></StackLayout>
Don't forget to add local reference in xaml:
xmlns:local="clr-namespace:YourprojectNameSpace"
===================================Update====================================
If want to change color with binding property by viewmodel , have a try with IValueConverter:
public class StringToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string valueAsString = value.ToString();
switch (valueAsString)
{
case (""):
{
return Color.Default;
}
case ("Accent"):
{
return Color.Accent;
}
default:
{
return Color.FromHex(value.ToString());
}
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
Config.cs should be modified as follow:
public class Config : INotifyPropertyChanged
{
private string mycolor;
public string MyColor
{
get { return mycolor; }
set
{
mycolor = value;
OnPropertyChanged("MyColor");
}
}
public Config (){
mycolor = "#00FF00"; // can set a defalut color here
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And in Xaml :
<ContentPage.Resources>
<ResourceDictionary>
<local:StringToColorConverter x:Key="StringToColorConverter"/>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout BackgroundColor="{Binding MyColor, Converter={StaticResource StringToColorConverter}}"></StackLayout>
Finally , ContentPage need to bind Model Config.cs:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
Config config = new Config();
this.BindingContext = config;
//MyColor can be modified runtime
config.MyColor = "#00FF00";
}
}
When you read about x:Static it directly says:
x:Static accesses one of the following:
a public static field
a public static property
a public constant field
an enumeration member.
since your properties do not meet the criteria mentioned above it does not work!
Related
In order to create my GUI more dynamically I like to do a binding in XAML which I defined in Code:
Edit:
I do not want call SetBinding() in code. I want to set the binding in XAML.
Code:
public class SPSProperty
{
public string LabelContent { get; private set; }
public string PropertyPath { get; private set; }
public Binding Binding { get; private set; }
public SPSProperty (INotifyPropertyChanged viewModel,string propertyPath, string labelContent)
{
LabelContent = labelContent;
PropertyPath = propertyPath;
Binding = new Binding(propertyPath);
Binding.Source = viewModel;
}
}
ViewModel:
public class MainWindowViewModel:BindableBase
{
public SPSProperty Property { get; set; }
public MainWindowViewModel()
{
Property = new SPSProperty(this, "Test_Property", "Test Property");
}
private string _Test_Property;
public string Test_Property
{
get { return _Test_Property; }
set { SetProperty(ref _Test_Property, value); }
}
}
How can I use the binding in XAML?
TextBox Text="{Binding Property.Binding}" <=This does of course not work.
I created a Behaviour for my Textbox.
class DynamicBindingBehaviour: Behavior<TextBox>
{
public static readonly DependencyProperty DynamicBindingProperty =
DependencyProperty.Register("DynamicBinding", typeof(Binding), typeof(DynamicBindingBehaviour), new FrameworkPropertyMetadata());
public Binding DynamicBinding
{
get { return (Binding)GetValue(DynamicBindingProperty); }
set { SetValue(DynamicBindingProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.SetBinding(TextBox.TextProperty, DynamicBinding);
}
}
And use it the in XAML by:
<TextBox DataContext="{Binding Path=Property}" >
<i:Interaction.Behaviors>
<local:DynamicBindingBehaviour DynamicBinding="{Binding Binding}"/>
</i:Interaction.Behaviors>
</TextBox>
I have two views : MainView and DetailView. I have a list of items to display and when user select an item and I am passing item properties to DetailViewModel and user could able to update these values.
Everything works so far, but I wonder how am I passing back to updated values to the MainViewModel ?
MainViewModel.cs
public MainViewModel SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
ShowViewModel<DetailViewModel>(
new DetailViewModel.Parameter
{
Date = Date,
Age = _selectedItem.Age,
Category = _selectedItem.Category,
Discount = _selectedItem.Discount,
}
);
RaisePropertyChanged(() => SelectedItem);
}
}
DetailViewModel.cs
public class DetailViewModel: MvxViewModel
{
public double Age { get; set; }
public double Category { get; set; }
public double Discount { get; set; }
public class Parameter
{
public DateTime Date { get; set; }
public double Age { get; set; }
public int Category{ get; set; }
public double Discount { get; set; }
}
public void Init(Parameter param)
{
Age = param.Age;
Category = param.Category;
Discount = param.Discount ;
}
}
One way to pass variables between ViewModels is a Messenger based solution.
MvvmCross Messenger can be found in NuGet.
MainViewModel
private readonly IMvxMessenger _messenger;
private readonly MvxSubscriptionToken _token;
public MainViewModel(IMvxMessenger messenger) {
_messenger = messenger;
_token = messenger.Subscribe<SelectedItemMessage>(OnMessageReceived);;
}
private void OnMessageReceived(SelectedItemMessage obj)
{
SelectedItem = obj.SelectedItem;
}
DetailViewModel
private readonly IMvxMessenger _messenger;
public DetailViewModel(IMvxMessenger messenger) {
_messenger = messenger;
}
public void YourUpdateMethod() {
var message = new SelectedItemMessage(this, SelectedItem); //SelectedItem assumed it is a ViewModel property.
_messenger.Publish(message, typeof(SelectedItemMessage));
}
SelectedItemMessage
public class SelectedItemMessage : MvxMessage
{
public SelectedItemMessage(object sender, SelectedItem selectedItem) : base(sender)
{
SelectedItem = selectedItem;
}
public SelectedItem SelectedItem { get; set; }
}
Take a look at http://slodge.blogspot.nl/2013/05/n9-getting-message-n1-days-of-mvvmcross.html for a full guide to MvvmCross Messenges.
Edit using age and category in Message
public SelectedItemMessage(object sender, double age, int category) : base(sender)
{
Age = age;
Category = category;
}
public double Age { get; set; }
public int Category{ get; set; }
}
Changing the MainViewModel OnMessageReceived method
private void OnMessageReceived(SelectedItemMessage obj)
{
Age = obj.Age;
Category= obj.Category;
}
Why not just keep a reference to DetailViewModel when you create it in MainViewModel? Then any values changed in the DetailViewModel instance will be available via that reference in MainViewModel.
private DetailViewModel _detailVM;
public MainViewModel SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
_detailVM = new DetailViewModel.Parameter {
Date = Date,
Age = _selectedItem.Age,
Category = _selectedItem.Category,
Discount = _selectedItem.Discount
};
ShowViewModel<DetailViewModel>(_detailVM);
RaisePropertyChanged(() => SelectedItem);
}
}
please tell me best way to implement many duplicate INotifyPropertyChanged.
I have a MainClass that has 10 children, every child has six field and every field must fired property change when own value changed.
this my code but not work:
public class BaseModel
{
public string S1 { get; set; }
public string S2 { get; set; }
public string S3 { get; set; }
public string S4 { get; set; }
public string S5 { get; set; }
public string S6 { get; set; }
}
and I use a class named ViewModelBase to implement INotifyPropertyChanged.
in second step use a class to implement duplicate INotifyPropertyChanged:
public class ImplementBaseModel : ViewModelBase
{
private readonly BaseModel _baseModel;
public ImplementBaseModel()
{
_baseModel = new BaseModel();
}
public string S1
{
get { return _baseModel.S1; }
set
{
if (_baseModel.S1 == value)
return;
_baseModel.S1 = value;
base.OnPropertyChanged("S1");
}
}
public string S2
{
get { return _baseModel.S2; }
set
{
if (_baseModel.S2 == value)
return;
_baseModel.S1 = value;
base.OnPropertyChanged("S2");
}
}
// other code...
}
then a model has 10 of this class:
public class MidClass
{
public ImplementBaseModel ImplementBaseModel1 { get; set; }
public ImplementBaseModel ImplementBaseModel2 { get; set; }
// other field
public ImplementBaseModel ImplementBaseModel10 { get; set; }
public MidClass()
{
ImplementBaseModel1 = new ImplementBaseModel();
ImplementBaseModel2 = new ImplementBaseModel();
// ....
ImplementBaseModel10 = new ImplementBaseModel();
}
}
OK finish code! now please tell me why some property not fired when value change? is a best way to implement this code?
In your setters, you never actually set the value. Use:
public string S1
{
get { return _baseModel.S1; }
set
{
if (_baseModel.S1 == value)
return;
baseModel.S1 = value;
OnPropertyChanged("S1");
}
}
Note that I removed the base from OnPropertyChanged. It isn't normal to invoke the PropertyChanged event in this way.
All NotifyPropertyChanged does is cause every binding to perform a "get" on their bound property. If the backing field is never updated, they will just get the same data.
as a shortcut, you could also create a local method like
bool UpdateAndRaiseIfNecessary( ref string baseValue, string newValue, [CallerMemberName] string propertyName = null)
{
if (baseValue != newValue)
{
baseValue = newValue;
OnPropertyChanged( propertyName );
return true;
}
return false;
}
and then all of the setters would be like this:
set
{
this.UpdateAndRaiseIfNecessary( ref _baseModel.S1, value );
}
This is using C# and MVVM.
At the moment I have a bunch of buttons that are created on to a panel via binding an ObservableCollection<Module> called ModuleCollection.
module is defined as:
public string ModuleName { get; private set; }
public string ModuleAbbreviation { get; private set; }
public bool ModuleDisabled { get; private set; }
public DateTime ModuleLicenseDate { get; private set; }
A label below each button gets set to ModuleName and the Content property of the button gets set to ModuleAbbreviation.
I also have a current_user object that holds a ObservableCollection<UserModule> called UserModules
A UserModule is defined as:
public int Module_ID { get; set; }
public int User_Module_Access { get; set; }
Using the ModuleCollection and current_user.UserModules lists, I would like to enable the buttons under the follow scenarios:
If the Module.Disabled = false
and the Module.ModuleLicenseDate > Now()
and the UserModule.User_Module_Access > 0
Otherwise the button will be disabled.
Other things to note: is that UserModules may only have a subset of ModuleCollection and ModuleCollection will be static but the properties within UserModules will be refreshed from time to time.
My question is: How to I go about binding these two collections so that I can them create my buttons and set the IsEnabled property based on both?
[EDIT] 2013-12-07
<Button.IsEnabled>
<MultiBinding Converter="{Binding viewmodel:IsEnabledMultiValueConverter}">
<Binding Source="{Binding ModuleID}" />
<Binding Source="{Binding ModuleDisabled}" />
<Binding Source="{Binding ModuleLicenseDate}" />
<Binding Source="{Binding current_user.user_modules}" />
</MultiBinding>
</Button.IsEnabled>
[EDIT2] 2013-12-09
When I change the module access level in a user module the event is getting fired. I change the value from accessible(1) to unaccessible(0), so in-turn the button should get disabled, however this does not happen.
public class UserModule : INotifyPropertyChanged
{
public UserModule(int Module_ID, int User_Module_Access)
{
this.Module_ID = Module_ID;
this.User_Module_Access = User_Module_Access;
}
private int _userModuleAccess;
public int Module_ID { get; set; }
public int User_Module_Access
{
get { return _userModuleAccess; }
set
{
_userModuleAccess = value;
MessageBox.Show("User_Module_Access");
RaisePropertyChanged("User_Module_Access");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
My thoughts are, that an event for ObservableCollection<UserModule> needs to occur when an item property changes. I have read that ObservableCollections don't do this, only adding, deleting and moving items. How to do this?
Some how in the User class?
public class User
{
public string UserName { get; set; }
public ObservableCollection<UserModule> UserModules = new ObservableCollection<UserModule>();
}
[Edit3] 2013-12-10 Redo - Implementing ItemsObservableObservableCollection
Launcher.XAML
<Button Content="{Binding ModuleAbbreviation}"
Style="{DynamicResource LauncherButton}"
Background="{Binding ModuleColor}"
FontSize="32" FontFamily="Tahoma" Width="130" Height="100"
Command="{Binding DataContext.LaunchCommand, RelativeSource={RelativeSource AncestorType=Window}}"
CommandParameter="{Binding ModuleName}">
<Button.Resources>
<viewmodel:IsEnabledMultiValueConverter x:Key="converter" />
</Button.Resources>
<Button.IsEnabled>
<MultiBinding Converter="{StaticResource converter}">
<Binding Path="ModuleID" />
<Binding Path="ModuleEnabled" />
<Binding Path="ModuleLicenseDate" />
<Binding ElementName="gridModules" Path="DataContext.CurrentUser" />
</MultiBinding>
</Button.IsEnabled>
</Button>
LauncherViewModel.cs
class LauncherViewModel
{
public LauncherViewModel()
{
timer = new Timer(SetModuleAccess, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));
}
int pass = 0;
bool _isEnabled = false;
Timer timer;
private void SetModuleAccess(object state)
{
if (pass > 0)
{
if (_isEnabled)
_isEnabled = false;
else
_isEnabled = true;
foreach (Users.UserModule uModule in ups.model.ups_repository._current_user.UserModules)
{
if (uModule.Module_ID == 0)
{
if (_isEnabled == false)
uModule.User_Module_Access = 0;
else
uModule.User_Module_Access = 1;
}
}
if (pass == 2)
ups.model.ups_repository._current_user.UserModules.Add(new Users.UserModule(8, 1));
}
pass++;
}
public Users.User CurrentUser
{
get { return ups.model.ups_repository._current_user; }
}
public ObservableCollection<Module> ModuleCollection
{
get { return ModuleKey._module_objects; }
}
}
public class IsEnabledMultiValueConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
try
{
bool userHasAccess = false;
int ModuleID = (int)values[0];
bool ModuleEnabled = (bool)values[1];
string ModuleLicenseDate = (string)values[2];
Users.User user = values[3] as Users.User;
Users.UserModule userModule = user.UserModules.SingleOrDefault(um => um.Module_ID == ModuleID);
DateTimeFormatInfo dtfi = new DateTimeFormatInfo();
dtfi.ShortDatePattern = "yyyy-MM-dd";
dtfi.DateSeparator = "-";
DateTime MLicenseDate = System.Convert.ToDateTime(ModuleLicenseDate, dtfi);
if (userModule != null)
{
userHasAccess = userModule.User_Module_Access > 0;
}
return (ModuleEnabled && (MLicenseDate >= DateTime.Now) && userHasAccess);
}
catch
{
return false;
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
User.cs
public class User : INotifyPropertyChanged
{
public static User CreateNewUser()
{
return new User();
}
public User() {}
public int User_ID { get; set; }
public string Username { get; set; }
public string Name { get; set; }
public string Job_Title { get; set; }
public string Department { get; set; }
public string Company { get; set; }
public string Phone_Office { get; set; }
public string Phone_Mobile { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public DateTime Last_Login { get; set; }
public int Status { get; set; }
public int Session_Timeout { get; set; }
private ItemsObservableObservableCollection<UserModule> user_modules = new ItemsObservableObservableCollection<UserModule>();
public ItemsObservableObservableCollection<UserModule> UserModules
{
get
{
return user_modules;
}
set
{
user_modules = value;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
UserModule.cs
public class UserModule : INotifyPropertyChanged
{
public UserModule(int Module_ID)
{
this.Module_ID = Module_ID;
}
public UserModule(int Module_ID, int User_Module_Access)
{
this.Module_ID = Module_ID;
this.User_Module_Access = User_Module_Access;
}
private int _module_id;
private int _userModuleAccess;
public int Module_ID
{
get { return _module_id; }
set
{
_module_id = value;
RaisePropertyChanged("Module_ID");
}
}
public int User_Module_Access
{
get { return _userModuleAccess; }
set
{
_userModuleAccess = value;
RaisePropertyChanged("User_Module_Access");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
ups.cs
namespace ups.model
{
public static class ups_repository
{
public static Users.User _current_user = new Users.User();
public static void LoadUser(string Username, string Key, string Message)
{
...
}
}
}
At this point buttons are displaying, in the VM above I switch the first user module in the collection from enabled to disabled, each five seconds. I also add permission for the last in the collection. The buttons are not enabled and disabling the way they should.
You can try MultiBinding with MultiValueConverter on IsEnabled property of Button. The MultiValueConverter will take the Module bound to the Button; and the Module Collection of the current user (in the following example I sent the user itself) and do the testing to return a bool to indicate if the Button can be enabled or disabled.
<Window x:Class="WpfHowTo.ItemsControlTestHorn"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:WpfHowTo"
Title="ItemsControlTestHorn" Height="300" Width="300">
<Window.Resources>
<l:MultiValueConverter x:Key="multiValueConverter"/>
</Window.Resources>
<Grid Name="gridModules">
<ItemsControl ItemsSource="{Binding Path=Modules}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Button Content="{Binding ModuleAbbreviation}">
<Button.IsEnabled>
<MultiBinding Converter="{StaticResource multiValueConverter}">
<Binding Path="."/>
<Binding ElementName="gridModules" Path="DataContext.CurrentUser"/>
<!--<Binding RelativeSource="{RelativeSource AncestorType=Grid}" Path="DataContext.CurrentUser"/>-->
</MultiBinding>
</Button.IsEnabled>
</Button>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
The MultiValueConverter is something like this.
public class MultiValueConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool result = true;
Module module = values[0] as Module;
User user = values[1] as User;
bool userHasAccess = false;
UserModule userModule = user.UserModules.SingleOrDefault(um => um.Module_ID == module.Module_ID);
if (userModule != null)
{
userHasAccess = userModule.User_Module_Access == 1;
}
return result = ! module.ModuleDisabled && module.ModuleLicenseDate > DateTime.Now && userHasAccess;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Class definitions
public class Module
{
public int Module_ID { get; set; }
public string ModuleName { get; set; }
public string ModuleAbbreviation { get; set; }
public bool ModuleDisabled { get; set; }
public DateTime ModuleLicenseDate { get; set; }
}
public class UserModule
{
public int Module_ID { get; set; }
public int User_Module_Access { get; set; }
}
public class User
{
private ObservableCollection<UserModule> _userModules = new ObservableCollection<UserModule>();
public string UserName { get; set; }
public ObservableCollection<UserModule> UserModules
{
get
{
return _userModules;
}
}
}
I have come up with one more idea which is more simpler. However, I am leaving the first answer as an example for MultiBinding.
Here goes the new idea. I have created a new collection using the other two collections. This technique is now in line with the title of the question.
public IEnumerable<object> Modules
{
get
{
ObservableCollection<Module> modules = GetAllModules();
User currentUser = GetCurrentUser();
var accessibleModules = modules.GroupJoin
(
currentUser.UserModules, m => m.Module_ID, um => um.Module_ID,
(m, um) => new
{
ModuleName = m.ModuleName,
ModuleAbbreviation = m.ModuleAbbreviation,
IsModuleAccessible = !m.ModuleDisabled && m.ModuleLicenseDate > DateTime.Now && (um.Count() == 0 ? -1 : um.Single().User_Module_Access) == 1
}
);
return accessibleModules;
}
}
In the .xaml
<ItemsControl ItemsSource="{Binding Path=Modules}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Button Content="{Binding ModuleAbbreviation}" IsEnabled="{Binding IsModuleAccessible}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
ItemsObservableObservableCollection
The following class is intended to overcome the problem that ObservableCollection does not listen to the changes of the contained objects.
The pre-requisite is that the objects which are being added to this collection should implement INotifyPropertyChanged interface.
In short, this class registers to the items' PropertyChanged event and rises the CollectionChanged event of the ObservableCollection.
http://msdn.microsoft.com/en-us/magazine/dd252944.aspx
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Collections;
namespace VJ.Collections
{
/// <summary>
/// This class adds the ability to refresh the list when any property of
/// the objects changes in the list which implements the INotifyPropertyChanged.
///
/// </summary>
/// <typeparam name="T">
/// The type of elements in the collection.
/// </typeparam>
public class ItemsObservableObsrvableCollection<T> : ObservableCollection<T> where T : INotifyPropertyChanged
{
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
RegisterPropertyChanged(e.NewItems);
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
UnRegisterPropertyChanged(e.OldItems);
}
else if (e.Action == NotifyCollectionChangedAction.Replace)
{
UnRegisterPropertyChanged(e.OldItems);
RegisterPropertyChanged(e.NewItems);
}
base.OnCollectionChanged(e);
}
protected override void ClearItems()
{
UnRegisterPropertyChanged(this);
base.ClearItems();
}
private void RegisterPropertyChanged(IList items)
{
foreach (INotifyPropertyChanged item in items)
{
item.PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
}
}
private void UnRegisterPropertyChanged(IList items)
{
foreach (INotifyPropertyChanged item in items)
{
item.PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);
}
}
private void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
}
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