WPF ContextMenu - no String with ItemTemplate - c#

Hi I am new to WPF Development and run into a problem Regarding Binding al public variable to a TextBlock element.
<ListBox.ContextMenu>
<ContextMenu ItemsSource="{Binding ActionsView}">
<ContextMenu.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
Action View is a public Observable collection of Action Items each holds a name which is public accessible as Name. So normally there should be no Problem. If I am right clicking on my Item, I get an empty ContextMenu with the correct number of entry’s but without any text.
picture of the empty ContextMenu
public class Action : INotifyPropertyChanged
{
public string Name;
public ContextAction(string name)
{
Name = name;
}
public event PropertyChangedEventHandler PropertyChanged;
}
It would be really nice if somebody could help me with this problem.

The solution was setting the getters an setters that’s it :)
public class ContextAction : INotifyPropertyChanged
{
public string _name;
public ContextAction(string name)
{
_name = name;
}
public string Name
{
get { return _name; }
}
public event PropertyChangedEventHandler PropertyChanged;
}

You need to implement property, not a field for bindings to work. Like this:
public string Name { get; set };

Related

C# BindingList<> not updating a WPF Listbox on changed items

I am on a MVVM C# project.
I want to display a list of objects.
I want to add and remove items in this list and ALSO change items in this list.
So I choosed the BindingList<> over the ObservableCollection<>, which would not get noticed if an item has changed.
(I also tested the ObservableCollectionEx which is out there in the web, but this has the same behavior like the BindingList for me).
But the Listbox is not changing when items are changed.
(Adding and removing items is updated in the Listbox)
In my XAML
<ListBox DisplayMemberPath="NameIndex" ItemsSource="{Binding Profiles}" SelectedItem="{Binding SelectedProfile}">
or alternative with the ItemTemplate
<ListBox DockPanel.Dock="Right" ItemsSource="{Binding Profiles}" SelectedItem="{Binding SelectedProfile}" Margin="0,10,0,0">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding NameIndex}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In my ViewModel (ViewModelBase is implementing INotifyPropertyChanged etc)
public class ProfileListViewModel : ViewModelBase
{
private BindingList<Profile> profiles;
public BindingList<Profile> Profiles
{
get
{
return profiles;
}
set
{
profiles = value;
RaisePropertyChanged();
}
}
My items are also implementing INotifyPropertyChanged and I am calling OnPropertyChanged("Name") in my Setters.
My model
public class Profile : INotifyPropertyChanged
{
public Profile(){}
public int ProfileID { get; set; }
private string name;
public string Name
{
get
{
return name;
}
set
{
name = value;
OnPropertyChanged("Name");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Wiring the View with the ViewModel (BindingList is initialized before View)
ProfileListViewModel plvw= new ProfileListViewModel(message.Content);
var profileView = new ProfileListView(plvw);
profileView.ShowDialog();
In the View.xaml.cs
public ProfileListView(ProfileListViewModel plvw)
{
InitializeComponent();
DataContext = plvw;
}
When I am changing the name of an object then I get the ListChanged event to which I have subscribted in my ViewModel (Profiles.ListChanged += Profiles_ListChanged;) for testing BUT the items in the ListBox are NOT changing.
What am I doing wrong?
How can I get a updated Listbox?
Since your DisplayIndex is the computed property NameIndex, you need to call OnPropertyChanged("NameIndex") when its value changes due to a change in other properties, e.g.:
public string Name
{
get
{
return name;
}
set
{
name = value;
OnPropertyChanged("Name");
OnPropertyChanged("NameIndex");
}
}
Use
Profiles.ResetBindings() to bind it again.

string bound to TextBlock in ComboBox doesn't show up

I have a TextBlock in a ComboBox in a C# WPF project bound to a list of 'Envelope' items, which have a string 'Name' and a double 'Weight' property, the former of which I would like to see displayed in the TextBlock.
When I run my program, the ComboBox appears without any text in it. It properly has three unlabeled items in it, and if I view the ItemsSource or SelectedItem of the ComboBox they show the appropriate values, and other code which interacts with the SelectedItem of the ComboBox behaves properly. The only thing that does not work is that the TextBlock contains no text. If I replace the "{Binding Name}" with "au ghdfjlnvgmumar" then the appropriate garbled characters appear in the ComboBox, so it is definitely a problem with the binding. What is the problem, and how can I get around it?
Relevant code:
xaml:
<ComboBox Name="EnvelopeList" HorizontalAlignment="Center" Width="200" >
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
C#:
//main window code
public MainWindow()
{
InitializeComponent();
envelopes = new List<Envelope>();
envelopes.Add(new Envelope("TEST", 0));
envelopes.Add(new Envelope("HI", 10));
EnvelopeList.ItemsSource = envelopes;
}
//Envelope class
class Envelope
{
public string Name;
public double Weight;
public Envelope()
{
Name = "[None]";
Weight = 0;
}
public Envelope(string n, double w)
{
Name = n;
Weight = w;
}
public override string ToString()
{
return Name;
}
}
When DataBinding, you can only bind to Properties. Also, you need to update your properties with a PropertyChangedEvent. Otherwise, if you change your property after the initial binding it won't update the UI.
You need to use on property changed and a property
public class Envelope: ModelBase
{
private string _name;
public string Name
{
get { return _name; }
set { _name= value; OnPropertyChanged("Name"); }
}
}
public class ModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propName));
}
}
}
Finally, I notice that you're setting ItemsSource directly. Instead, you want to set your View's DataContext property and then bind to your ItemsSource
Here is a MSDN article on DataBinding that will teach you how to do it properly.
Name is a field, you can only bind to properties.

Bind collection to combobox

I have this combobox
<ComboBox Height="30" SelectedIndex="0" Margin="5 3 5 3" Width="170" ItemsSource="{Binding WonderList}" SelectedValuePath="selectedWonder">
<ComboBox.ItemTemplate>
<DataTemplate>
<WrapPanel>
<Image Source="{Binding Path}" Height="20"></Image>
<Label Content="{Binding Name}" Style="{StaticResource LabelComboItem}"></Label>
</WrapPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
where I want to show items as an image plus a text.
This is the business class for the objects in the item list
public class Wonder: INotifyPropertyChanged
{
private string name;
private string path;
public event PropertyChangedEventHandler PropertyChanged;
#region properties, getters and setters
public String Name { get; set; }
public String Path { get; set; }
#endregion
public Wonder(string name, string path)
{
this.name = name;
this.path = path;
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
and the code behind of the window
public class Window1 {
public List<Wonder> WonderList;
public Window1()
{
InitializeComponent();
WonderList = new List<Wonder>();
WonderList.Add(new Wonder("Alexandria", "Resources/Images/Scans/Wonders/Alexandria.jpg"));
WonderList.Add(new Wonder("Babylon", "Resources/Images/Scans/Wonders/Babylon.jpg"));
}
}
I´m pretty new to this xaml "magic" and guess I dont understand correctly how the data binding works, I think that with ItemsSource="{Binding WonderList}" it should take the collection with that name (from the code behind) and show their Name and Path, but it shows an empty list.
If I do Combo1.ItemsSource = WonderList; in the code behind (I prefer to use the xaml and avoid the code behind), it shows two blank slots but still don´t know how to show the items.
Can you point me in the right direction?
Thanks
If you want to bind like this ItemsSource="{Binding WonderList}" you have to set the DataContext first.
public Window1()
{
...
this.DataContext = this;
}
Then Binding will find the WonderList in Window1 but only if it is a property too.
public List<Wonder> WonderList { get; private set; }
Next: It is useless to bind to property Name if you assign your value to private field name. Replace your constructor with
public Wonder(string name, string path)
{
this.Name = name;
this.Path = path;
}
Next: Your auto properties ({ get; set; }) will not notify for changes. For this you have to call OnPropertyChanged in setter. e.g.
public String Name
{
get { return name; }
set
{
if (name == value) return;
name = value;
OnPropertyChanged("Name");
}
}
Same thing for WonderList. If you create the List to late in constructor it could be all bindings are already resolved and you see nothing.
And finally use ObservableCollection if you want to notify not for a new list but a new added item in your list.
You are not doing the correct way. Simply saying, you should have a Wonders class holding an ObservableCollection property, which is bound to ComboBox's ItemsSource. You should read MSDN:
http://msdn.microsoft.com/en-us/library/ms752347.aspx

Observable collection item property changed

I have a column class which uses view model base to implement INotifyPropertyChanged (lazy I know):
public class Column : ViewModelBase
{
public string ColumnName { get; set; }
public bool Anonymize { get; set; }
}
And then a list of columns:
public class Columns : ObservableCollection<Column>
{
}
In my view model I have a property columns and I am binding that to a combo box with a checkbox and textblock:
private Columns _tableColumns;
public Columns TableColumns
{
get
{
return _tableColumns;
}
set
{
_tableColumns = value;
OnPropertyChanged("TableColumns");
}
}
<ComboBox Name="cbColumns" ItemsSource="{Binding TableColumns}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Anonymize, Mode=TwoWay}" />
<TextBlock Text="{Binding ColumnName}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
When I change the Anonymize property through the checkbox on an item, how do make the Columns property change in the view model to reflect this?
Your Column class needs to implement INotifyPropertyChanged (which you say it does). You also need to raise that event it when the value of Anonymize changes (which you don't).
If you want to change the Anonymize property only from the UI, you are done. If you'd like to see the changes(from the backend) on the UI, you have to implement the INotifyPropertyChanged interface in the Column class.
public class Column : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string columnName;
public bool anonymize;
public string ColumnName
{
get { return columnName; }
set
{
columnName = value; RaiseOnPropertyChanged("ColumnName");
}
}
public bool Anonymize
{
get { return anonymize; }
set { anonymize = value; RaiseOnPropertyChanged("Anonymize"); }
}
public void RaiseOnPropertyChanged(string propertyName)
{
var eh = PropertyChanged;
if (eh != null)
eh(this, new PropertyChangedEventArgs(propertyName));
}
}
When the Anonymize state changes it will need to notify the view model that it needs to modify the collection of columns. The way I've solved this before is to add a CheckChanged event to the Column class that is raised when Anonymize. The the view model subscribes to the event after it creates the Column object but it is added to the Columns collection. When CheckChanged is raised the view model adds/removes the item from the Columns collection.

Binding set to CheckBoxes in WPF

using Entity Framework (C#) I have a User class which has ONE:MANY mapping to the UserRight class (simply, user has a set of rights). Each right is identified by a string. And now, because the maximum number of possible rights is finite (<10) I'd like to have 10 CheckBoxes and edit the subset of rights for a given user manually.
What is the nice way to do it?
James
Create a RightViewModel class to contain user rights:
public class RightViewModel : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
Change("Name");
}
}
private bool _hasRight;
public bool HasRight
{
get { return _hasRight; }
set
{
_hasRight = value;
Change("HasRight");
}
}
public void Change(string strPropertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(strPropertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
Create a similar class for your user, containing a member Rightsof type ObservableCollection<RightViewModel>.
In you XAML, use an ItemsControl:
<ItemsControl ItemsSource="{Binding Rights}"
ItemTemplate="{StaticResource RightTemplate}"/>
And a template definition:
<DataTemplate x:Key="RightTemplate">
<CheckBox Content="{Binding Name}" IsChecked="{Binding HasRight, Mode=TwoWay}"/>
</DataTemplate>
Mode=TwoWay makes the binding update your RightViewModel instance.
Define the ItemsControl's ItemsPanel if you need to display your checkboxes with a different layout.
Finally set your user as the DataContext of your container.

Categories

Resources