I have one main outstanding issue, I know now how to databind to lists and individual items, but there is one more problem I have with this, I need to bind a listbox to some properties that are in a Class.
For Example I have two Properties that are bound in some XAML in a class called Display:
Public Property ShowEventStart() As Visibility
Public Property ShowEventEnd() As Visibility
I can use these in my XAML but I want them to be in a ListBox/Drop-down List, how can I have my properties show in this list and be able to change their values, does it have to be a List to be in a List?
I just want to be able to modify these properties from a Drop-down list to toggle the Visibility of the ShowEventStart and ShowEventEnd Property Values using the Checkboxes in the Drop-down List.
Plus this must be a Silverlight 3.0 solution, I cannot figure out how to have something that can be bound in the XAML which is not a list and then bound it as a list to modify these items!
I just need a list of Checkboxes which alter the values of the Class Properties such as ShowEventStart and ShowEventEnd, there are other properties but this will be a start.
You can create a PropertyWrapper class and in you window code behind expose a property that returns a List<PropertyWrapper> and bind your ListBox.ItemsSource to it.
public class PropertyWrapper
{
private readonly object target;
private readonly PropertyInfo property;
public PropertyWrapper(object target, PropertyInfo property)
{
this.target = target;
this.property = property;
}
public bool Value
{
get
{
return (bool) property.GetValue(target, null);
}
set
{
property.SetValue(target, value, null);
}
}
public PropertyInfo Property
{
get
{
return this.property;
}
}
}
your window code behind:
public partial class Window1 : Window, INotifyPropertyChanged
{
public Window1()
{
InitializeComponent();
properties = new List<PropertyWrapper>
{
new PropertyWrapper(this, typeof(Window1).GetProperty("A")),
new PropertyWrapper(this, typeof(Window1).GetProperty("B")),
};
this.DataContext = this;
}
private List<PropertyWrapper> properties;
public List<PropertyWrapper> Properties
{
get { return properties; }
}
private bool a;
private bool b = true;
public bool A
{
get
{
return a;
}
set
{
if (value != a)
{
a = value;
NotifyPropertyChange("A");
}
}
}
public bool B
{
get
{
return b;
}
set
{
if (value != b)
{
b = value;
NotifyPropertyChange("B");
}
}
}
protected void NotifyPropertyChange(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged.Invoke(
this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
your window markup:
<ListBox ItemsSource="{Binding Path=Properties}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Property.Name}"/>
<CheckBox IsChecked="{Binding Path=Value}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Hope this helps
I tried to mock up something similar. See how this works for you and let me know if I misunderstood the question.
From MainPage.xaml:
<StackPanel Orientation="Horizontal">
<ListBox SelectedItem="{Binding ShowControls, Mode=TwoWay}" x:Name="VisibilityList"/>
<Button Content="Test" Visibility="{Binding ShowControls}"/>
<CheckBox Content="Test 2" Visibility="{Binding ShowControls}"/>
</StackPanel>
The code behind MainPage.xaml.cs:
public partial class MainPage : UserControl
{
VisibilityData visibilityData = new VisibilityData();
public MainPage()
{
InitializeComponent();
VisibilityList.Items.Add(Visibility.Visible);
VisibilityList.Items.Add(Visibility.Collapsed);
this.DataContext = visibilityData;
}
}
And the data class:
public class VisibilityData : INotifyPropertyChanged
{
private Visibility showControls = Visibility.Visible;
public Visibility ShowControls
{
get { return showControls; }
set
{
showControls = value;
OnPropertyChanged("ShowControls");
}
}
private void OnPropertyChanged(string p)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
public event PropertyChangedEventHandler PropertyChanged;
}
When you run that code you should get a ListBox with the options Visible and Collapsed and when you choose an option you should see the visibility of the button and checkbox is changed to reflect your choice. Let me know if that's not what you were looking for.
After thinking about this the solution occured to me, which is to construct the List in XAML then using a Converter on the Checkboxes to convert their Boolean IsChecked to the Property Visibility Property in the Class.
You can create a list such as:
<ComboBox Canvas.Left="6" Canvas.Top="69" Width="274" Height="25" Name="Display">
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Path=Display.EventStart,Mode=TwoWay,Converter={StaticResource VisibilityConverter}}"/>
<TextBlock Text="Event Start"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Path=Display.EventEnd,Mode=TwoWay,Converter={StaticResource VisibilityConverter}}"/>
<TextBlock Text="Event End"/>
</StackPanel>
</ComboBox>
I can then Bind to the Two Properties I want (this example from the actual application).
Related
I have an ObservableCollection that gets it's data from a DataTable that is populate from a Postgres Database. I need to bind this ObservableCollection to a ComboBoxColumn in a DataGrid. I have seen quite a lot of examples on how to do this, yet I'm constantly missing something.
Edit: This is the new updated code and it is working except for the INotifyPropertyChanged that I have set only to "name" (yet)
namespace Country_namespace
{
public class CountryList : ObservableCollection<CountryName>
{
public CountryList():base()
{
// Make the DataTables and fill them
foreach(DataRow row in country.Rows)
{
Add(new CountryName((string)row.ItemArray[1], (int)row.ItemArray[0]));
}
}
}
public class CountryName: INotifyPropertyChanged
{
private string name;
private int id_country;
public event PropertyChangedEventHandler PropertyChanged;
public CountryName(string country_name, int id)
{
this.name = country_name;
this.id_country = id;
}
public string Name
{
get { return name; }
set {
name = value;
OnPropertyChanged("CountryName");
}
}
public int idcountry
{
get { return id_country; }
set { id_country = value; }
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
}
XAML:
xmlns:c="clr-namespace:Country_namespace"
<Windows.Resources>
<c:CountryList x:Key="CountryListData"/>
</Windows.Resources>
DataGrid Column:
<dg:DataGridTemplateColumn Header="country">
<dg:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Source={StaticResource CountryListData}}" DisplayMemberPath="Name"></ComboBox>
</DataTemplate>
</dg:DataGridTemplateColumn.CellTemplate>
</dg:DataGridTemplateColumn>
first of all. you can just bind to public properties.
country_ seems no public property.
second if binding not work you always have to check datacontext first and binding path second. you can use Snoop to do this at runtime
EDIT:
you did not post your itemssource for your grid. so some assumptions here.
<DataGrid ItemsSource="{Binding MySource}">
...
<ComboBox ItemsSource="{Binding MySourcePropertyForCountries}"/>
--> this would work when your MySource object item has a public property MySourcePropertyForCountries.
but if your want to bind your combobox to a list wich is outside the MySource object. then you have to use some kind relativeSourcebinding or elementbinding.
<DataGrid x:Name="grd" ItemsSource="{Binding MySource}">
...
<ComboBox ItemsSource="{Binding ElementName=grd, Path=DataContext.MyCountries}"/>
--> this would work when the datacontext of the datagrid has a property MyCountries
I have a List<String> in a custom user control. Whenever the List<String> is modified, I want the custom user control to have checkboxes for every item in the List<String>. My original idea (being used to Java) was to just add the checkboxes and remove them directly.
But... I know C# can do better than that. Is there some way I can "bind" the strings to the UI so they show up as checkboxes? (or any other method that works?)
You can do that easily by binding an ItemsControl to the list of strings:
<ItemsControl ItemsSource="{Binding Strings}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
However that's not very useful, because you have no easy way to access the state of the CheckBox... A better option would be to wrap the string in a class that also exposes a bool property:
public class CheckableItem<T> : INotifyPropertyChanged
{
public CheckableItem(T value)
{
_value = value;
}
private T _value;
public T Value
{
get { return _value; }
set
{
_value = value;
OnPropertyChanged("Value");
}
}
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set
{
_isChecked = value;
OnPropertyChanged("IsChecked");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
You can then expose a list of CheckableItem<string> instead of a list of strings, and change the XAML as follows:
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Value}" IsChecked="{Binding IsChecked}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
If you need to test if the CheckBox is checked, just test the IsChecked property in the CheckableItem<T> class.
If you need the CheckBox to have 3 states (checked/unchecked/indeterminate), just declare IsChecked as bool? instead of bool.
I have list of checkboxes on a window specifying some items to be ordered. I need to first disable the Order button when the windows loads and enable it after selecting/check some items(checkboxes) and vice versa. I have bind the IsChecked property of the checkbox.
Edit Import from OP comment:-
I have only one checkbox in the ItemsControl. and I have bind the ItemsControl's ItemsSource to List. that way we can show multiple checkboxes as per the items in the List.
Here is the code:
<ItemsControl ItemsSource="{Binding FavoriteItems}" Margin="80,0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<StackPanel>
<Grid>
<CheckBox IsChecked="{Binding IsHouseholdSelected}" Content="{Binding SubCategoryName}" Grid.ColumnSpan="1" FontFamily="Calibri" FontWeight="Bold" />
</Grid>
</StackPanel>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
Below is a sample code that could help you out. Basically, the key here is I had the Items in the list implicitly notify its parent ViewModel's Command object to raise the CanExecuteChanged event every time the IsChecked property changes. (Also, I'm using "DelegateCommand" here, which is just the same as "RelayCommand").
ViewModels:
public class ViewModel : INotifyPropertyChanged
{
public DelegateCommand MyCommand { get; set; }
private ObservableCollection<ItemViewModel> items = new ObservableCollection<ItemViewModel>();
public ObservableCollection<Item> Items
{
get { return this.items; }
}
public ViewModel()
{
this.items.Add(new ItemViewModel(this) { IsChecked = false, Text = "Item 1" });
this.items.Add(new ItemViewModel(this) { IsChecked = false, Text = "Item 2" });
this.items.Add(new ItemViewModel(this) { IsChecked = false, Text = "Item 3" });
this.MyCommand = new DelegateCommand(this.CanExecute, this.Execute);
}
public void Execute(object parameter)
{
MessageBox.Show("Executed");
}
public bool CanExecute(object parameter)
{
return (this.items.Count == this.items.Count((x) => x.IsChecked));
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
#endregion
}
public class ItemViewModel
{
private ViewModel parent;
private bool isChecked;
public string Text { get; set; }
public bool IsChecked
{
get { return this.isChecked; }
set
{
this.isChecked = value;
if (this.parent.MyCommand != null)
this.parent.MyCommand.OnCanExecuteChanged(null);
}
}
public Item(ViewModel parent)
{
this.parent = parent;
}
}
View:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication2"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<DockPanel>
<Button DockPanel.Dock="Bottom" Command="{Binding MyCommand}">Test</Button>
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked}" Content="{Binding Text}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DockPanel>
</Window>
Bind a command to the button and implement the CanExecute method to check the status of the checkboxes and either enable or disable the button and use the Execute method to invoke the functionality that you want on the button.
MVVM RelayCommand
CanExecute on MSDN
EDIT: Here is some source code of how to implement a RelayCommand. The RelayCommand class can be found at the first link provided above. I'm assuming that you know how to hook up the DataContext to the ViewModel implementation.
<StackPanel>
<CheckBox Name="MyCheckBox" Content="Some CheckBox"
IsChecked="{Binding MyCheckBoxChecked}"/>
<Button Content="Click me" Command="{Binding MyCommand}"/>
</StackPanel>
public class OrderViewModel
{
private RelayCommand MyRelayCommand;
public OrderViewModel()
{
MyRelayCommand = new RelayCommand(Execute, CanExecute);
MyCheckBoxChecked = false;
}
public RelayCommand MyCommand
{
get { return MyRelayCommand; }
}
public bool MyCheckBoxChecked { get; set; }
private bool CanExecute(object o)
{
// Here I'm just checking the property we've bound to but you can put
// anything in here that will return a bool, including a check of any/all
// of the checkboxes you may need to check
return MyCheckBoxChecked;
}
private void Execute(object o)
{
Console.WriteLine(#"Executing ...");
}
}
i have customized ListBox declared in XAML:
<ListBox x:Name="uicMDSQonfServer">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal"
Margin="0,5,0,5">
<CheckBox IsChecked="{Binding RelativeSource={TemplatedParent},
Path=Activated}" />
<ContentPresenter Content="{Binding RelativeSource={TemplatedParent},
Path=Content}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I need to dsiplay and interop with generic List, where T is:
public class QonfServer: QonfBase, INotifyPropertyChanged
{
private string ip;
private bool activated;
public string Ip {
get { return ip; }
}
public bool Activated
{
get { return activated; }
set
{
if (activated == value)
return;
activated = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Activated"));
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
QonfBase is pretty simple Base class:
public class QonfBase
{
private int id;
public int ID { get; set; }
}
When i turn Activated property programmatically, checkbox do not change state. Debug: PropertyChanged = null. Anybody know, what is incorrect?
One obvious problem meets the eye: TemplatedParent is for use with ControlTemplates. Since you're using a DataTemplate, this should work:
<CheckBox IsChecked="{Binding Activated}" />
<ContentPresenter Content="{Binding Content}"/>
I didn't notice any problems with the C#.
I am trying to successfully TwoWay bind an ObservableCollection to TextBoxes in a DataTemplate. I can get the data to display properly, but I am unable to change the list data through the UI. I have a Model class named 'model' which contains an ObservableCollection named 'List'. The class implements the INotifyPropertyChanged interface. Here is the xaml for the shell. The DataContext for Window1's grid is set to "theGrid.DataContext=model"
<Window x:Class="BindThat.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:BindThat"
Title="Window1" Height="300" Width="300">
<StackPanel x:Name="theGrid">
<GroupBox BorderBrush="LightGreen">
<GroupBox.Header>
<TextBlock Text="Group" />
</GroupBox.Header>
<ItemsControl ItemsSource="{Binding Path=List}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=., Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</GroupBox>
</StackPanel>
This is the code for the Model class:
class Model : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
private ObservableCollection<string> _list = new ObservableCollection<string>();
public ObservableCollection<string> List
{
get { return _list; }
set
{
_list = value;
NotifyPropertyChanged("List");
}
}
public Model()
{
List.Add("why");
List.Add("not");
List.Add("these?");
}
}
Could anyone advise if I am going about this the correct way?
You need a property to bind two way, so string is not good for this.
Wrap it in a string object, like this:
public class Model
{
public ObservableCollection<StringObject> List { get; private set; }
public Model()
{
List = new ObservableCollection<StringObject>
{
new StringObject {Value = "why"},
new StringObject {Value = "not"},
new StringObject {Value = "these"},
};
}
}
public class StringObject
{
public string Value { get; set; }
}
and bind to Value property instead of "."
Also, you don't need to notify of a change in observable collection, so until your model has some other propertis of its own, it does not need to have INotifyPropertyChange. If you want your ItemsControl react to changes in the individual StringObjects, then you should add INotifyPropertyChanged to a StringObject.
And yet again, two way binding is default, so you need only
<TextBox Text="{Binding Path=Value}" />
in your binding.
I believe you need to derive your collection items from DependencyObject for TwoWay binding to work. Something like:
public class DependencyString: DependencyObject {
public string Value {
get { return (string)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(string), typeof(DependencyString), new UIPropertyMetadata(""));
public override string ToString() {
return Value;
}
public DependencyString(string s) {
this.Value = s;
}
}
public class Model {
private ObservableCollection<DependencyString> _list = new ObservableCollection<DependencyString>();
public ObservableCollection<DependencyString> List {
get { return _list; }
}
public Model() {
List.Add(new DependencyString("why"));
List.Add(new DependencyString("not"));
List.Add(new DependencyString("these?"));
}
}
...
<StackPanel x:Name="theGrid">
<GroupBox BorderBrush="LightGreen">
<GroupBox.Header>
<TextBlock Text="Group" />
</GroupBox.Header>
<ItemsControl ItemsSource="{Binding Path=List}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=Value, Mode=TwoWay}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</GroupBox>
</StackPanel>
xaml view:
<ItemsControl ItemsSource="{Binding List}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
in code behind in the constructor:
DataContext = new ViewModel();
in ViewModel Class:
class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
private ObservableCollection<StringObject> _List = new ObservableCollection<StringObject>();
public ObservableCollection<StringObject> List
{
get { return _List; }
set
{
_List = value;
NotifyPropertyChanged("List");
}
}
public ViewModel()
{
List = new ObservableCollection<StringObject>
{
new StringObject {Value = "why"},
new StringObject {Value = "not"},
new StringObject {Value = "these"}
};
}
}
public class StringObject
{
public string Value { get; set; }
}
Be careful with a collection with type string it doesn't work, you have to use an object => StringObject