combobox selection changed behaviour? - c#

I am developing a WPF application in which i have a ComboBox,like this
<ComboBox SelectedIndex="1" Grid.Column="2" Grid.Row="1" ItemsSource="{Binding VipCodes}"
SelectedItem="{Binding SelectedVipCode,Mode=OneWay}" Style="{StaticResource DefaultComboBoxStyle}" x:Name="vipCode" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Description}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
after loading the page when the selection changed, i need to update a value of a property.
I did like hooking up the selection changed event and set the value.
But when the page loaded, the event fires and the value of a property is set.
how can i bypass this?

Just set a global variable if you absolutely have to. var SkipOnce = true; and then on page load set it to false at the end. And then in your selection changed event add: if (SkipOnce==false) { //do stuff }

Described behavior can be achieved without event handlers in code behind, by ViewModel only. If DataContext of ComboBox is set to view model, then following code will do the job:
public MainWindowViewModel()
{
for (int i = 0; i < 10; i++)
{
_vipCodes.Add(new VipCode() { Description = i.ToString() });
}
SelectedVipCode = _vipCodes[3];
}
private ObservableCollection<VipCode> _vipCodes = new ObservableCollection<VipCode>();
public ObservableCollection<VipCode> VipCodes
{
get { return _vipCodes; }
}
private VipCode _selectedVipCode;
public VipCode SelectedVipCode
{
get { return _selectedVipCode; }
set
{
_selectedVipCode = value;
OnPropertyChanged();
}
}
protected void OnPropertyChanged([CallerMemberName] string property = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}

Related

Returning bound checkbox values using MVVM in a WPF form

I have an object that consists of a string and an array. The string populates a ComboBox and the array populates a ListView depending on the selected string value. Each line of the ListViewconsists of a TextBlock and a CheckBox.
On submit I want to be able to verify which items have been selected for further processing but there's a disconnect when using the MVVM approach. I currently have the DataContext of the submit Button binding to the ListView but only the first value is being returned upon submit (somewhere I need to save the selected values to a list I assume but I'm not sure where). I added an IsSelected property to the model which I think is the first step, but after that I've been grasping at straws.
Model
namespace DataBinding_WPF.Model
{
public class ExampleModel { }
public class Example : INotifyPropertyChanged
{
private string _name;
private string[] _ids;
private bool _isSelected;
public bool IsSelected
{
get => _isSelected;
set
{
if (_isSelected != value)
{
_isSelected = value;
RaisePropertyChanged("IsSelected");
}
}
}
public string Name
{
get => _name;
set
{
if (_name != value)
{
_name = value;
RaisePropertyChanged("Name");
}
}
}
public string[] IDs
{
get => _ids;
set
{
if (_ids != value)
{
_ids = value;
RaisePropertyChanged("IDs");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new
PropertyChangedEventArgs(property));
}
}
}
}
ViewModel
namespace DataBinding_WPF.ViewModel
{
public class ExampleViewModel : INotifyPropertyChanged
{
public ObservableCollection<Example> Examples
{
get;
set;
}
// SelectedItem in the ComboBox
// SelectedItem.Ids will be ItemsSource for the ListBox
private Example _selectedItem;
public Example SelectedItem
{
get => _selectedItem;
set
{
_selectedItem = value;
RaisePropertyChanged(nameof(SelectedItem));
}
}
// SelectedId in ListView
private string _selectedId;
public string SelectedId
{
get => _selectedId;
set
{
_selectedId = value;
RaisePropertyChanged(nameof(SelectedId));
}
}
private string _selectedCheckBox;
public string IsSelected
{
get => _selectedCheckBox;
set
{
_selectedCheckBox = value;
RaisePropertyChanged(nameof(IsSelected));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new
PropertyChangedEventArgs(property));
}
}
public void LoadExample()
{
ObservableCollection<Example> examples = new ObservableCollection<Example>();
examples.Add(new Example { Name = "Mark", IDs = new string[] { "123", "456" }, IsSelected = false });
examples.Add(new Example { Name = "Sally", IDs = new string[] { "789", "101112" }, IsSelected = false });
Examples = examples;
}
/* BELOW IS A SNIPPET I ADDED FROM AN EXAMPLE I FOUND ONLINE BUT NOT SURE IF IT'S NEEDED */
private ObservableCollection<Example> _bindCheckBox;
public ObservableCollection<Example> BindingCheckBox
{
get => _bindCheckBox;
set
{
_bindCheckBox = value;
RaisePropertyChanged("BindingCheckBox");
}
}
}
}
View
<UserControl x:Class = "DataBinding_WPF.Views.StudentView"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d = "http://schemas.microsoft.com/expression/blend/2008"
xmlns:local = "clr-namespace:DataBinding_WPF"
mc:Ignorable = "d"
d:DesignHeight = "300" d:DesignWidth = "300">
<Grid>
<StackPanel HorizontalAlignment = "Left" >
<ComboBox HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="120"
ItemsSource="{Binding Path=Examples}"
SelectedItem="{Binding SelectedItem}"
DisplayMemberPath="Name"/>
<ListView x:Name="myListView"
ItemsSource="{Binding SelectedItem.IDs}"
DataContext="{Binding DataContext, ElementName=submit_btn}"
SelectedItem="{Binding SelectedId}"
Height="200" Margin="10,50,0,0"
Width="Auto"
VerticalAlignment="Top"
Background="AliceBlue">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<CheckBox
Name="myCheckBox"
IsChecked="{Binding IsSelected,
RelativeSource={RelativeSource AncestorType=ListViewItem}}"
Margin="5, 0"/>
<TextBlock Text="{Binding}" FontWeight="Bold" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button HorizontalAlignment="Left" Height="20" Width="100"
Click="Submit" x:Name="submit_btn">Submit</Button>
</StackPanel>
</Grid>
</UserControl>
View.cs
namespace DataBinding_WPF.Views
{
/// <summary>
/// Interaction logic for StudentView.xaml
/// </summary>
public partial class StudentView : UserControl
{
public StudentView()
{
InitializeComponent();
}
private void Submit(object sender, EventArgs e)
{
var selectedItems = ((Button)sender).DataContext;
// process each selected item
// foreach (var selected in ....) { }
}
}
}
The ListView control already exposes a selected items collection as property SelectedItems.
private void Submit(object sender, RoutedEventArgs e)
{
var selectedIds = myListView.SelectedItems.Cast<string>().ToList();
// ...do something with the items.
}
However, I doubt that you want to do this in the code-behind, but rather in the view model. For this purpose, WPF offers the concept of commands.
MVVM - Commands, RelayCommands and EventToCommand
What you need is a relay command or delegate command (the name varies across frameworks). It encapsulates a method that should be executed for e.g. a button click and a method to determine whether the command can be executed as an object that can be bound in the view. Unfortunately, WPF does not provide an implementation out-of-the-box, so you either have to copy an implementation like here or use an MVVM framework that already provides one, e.g. Microsoft MVVM Tookit.
You would expose a property Submit of type ICommand in your ExampleViewModel and initialize it in the constructor with an instance of RelayCommand<T> that delegates to a method to execute.
public class ExampleViewModel : INotifyPropertyChanged
{
public ExampleViewModel()
{
Submit = new RelayCommand<IList>(ExecuteSubmit);
}
public RelayCommand<IList> Submit { get; }
// ...other code.
private void ExecuteSubmit(IList selectedItems)
{
// ...do something with the items.
var selectedIds = selectedItems.Cast<string>().ToList();
return;
}
}
In your view, you would remove the Click event handler and bind the Submit property to the Command property of the Button. You can also bind the SelectedItems property of the ListView to the CommandParameter property, so the selected items are passed to the command on execution.
<Button HorizontalAlignment="Left"
Height="20"
Width="100"
x:Name="submit_btn"
Command="{Binding Submit}"
CommandParameter="{Binding SelectedItems, ElementName=myListView}">Submit</Button>
Additionally, a few remarks about your XAML.
Names of controls in XAML should be Pascal-Case, starting with a capital letter.
You should remove the DataContext binding from ListView completely, as it automatically receives the same data context as the Button anyway.
DataContext="{Binding DataContext, ElementName=submit_btn}"
You can save yourself from exposing and binding the SelectedItem property in your ExampleViewModel, by using Master/Detail pattern for hierarchical data.
<Grid>
<StackPanel HorizontalAlignment = "Left" >
<ComboBox HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="120"
ItemsSource="{Binding Path=Examples}"
IsSynchronizedWithCurrentItem="True"
DisplayMemberPath="Name"/>
<ListView ItemsSource="{Binding Examples/IDs}"
SelectedItem="{Binding SelectedId}"
Height="200" Margin="10,50,0,0"
Width="Auto"
VerticalAlignment="Top"
Background="AliceBlue">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<CheckBox Name="myCheckBox"
IsChecked="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListViewItem}}"
Margin="5, 0"/>
<TextBlock Text="{Binding}"
FontWeight="Bold" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button HorizontalAlignment="Left"
Height="20"
Width="100"
Command="{Binding Submit}"
CommandParameter="{Binding SelectedItems, ElementName=myListView}">Submit</Button>
</StackPanel>
</Grid>
If the view's data context is bound to the view then remove the DataContext from the ListView.
You could remove the item template and instead use a GridView like:
<ListView.View>
<GridView >
<GridViewColumn Header="Selected" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsSelected}" Content="{Binding Name}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
Since the ItemSource is an Observable collection, there are several options to monitor changes in the checkboxes:
Add an event handler to the item changed event of the collection and then you can add the Name or the collection index to a local collection. e.g Examples[e.CollectionIndex].Name
Alternatively iterate over the observable collection and select those Examples where Selected = "true"

ListBox with DataTemplate recognize SelectedItem

I have a ListBox with a simple DataTemplate, a CheckBox, and a TextBox.
If the user checks a CheckBox I want to get this changed item, like the property SelectedItem of the ListBox.
How can I get the element from List2, which has changed?
MyListItem:
public class MyListItem2 : ReactiveObject
{
private string _name;
public string Name
{
get { return _name; }
set
{
this.RaiseAndSetIfChanged(ref _name, value, "Name");
}
}
private bool _isMarked;
public bool IsMarked
{
get { return _isMarked; }
set
{
this.RaiseAndSetIfChanged(ref _isMarked, value, "IsMarked");
}
}
}
View:
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DataTemplate.Views.MainWindow"
xmlns:viewsmodels="clr-namespace:DataTemplate.ViewModels;assembly=DataTemplate"
xmlns:dt="clr-namespace:DataTemplate;assembly=DataTemplate"
Title="DataTemplate" Width="700">
<Window.DataContext>
<viewsmodels:MainWindowViewModel />
</Window.DataContext>
<Grid ColumnDefinitions="250">
<ListBox Grid.Column="1" Items="{Binding List2}">
<ListBox.ItemTemplate>
<DataTemplate DataType="dt:MyListItem2">
<Grid ColumnDefinitions="50*,50*">
<CheckBox Grid.Column="0" Content="Mark" IsChecked="{Binding IsMarked}"/>
<TextBox Grid.Column="1" Text="{Binding Name}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
ViewModel:
public class MainWindowViewModel : ReactiveObject
{
public ObservableCollection<MyListItem2> List2 { get; set; }
public MainWindowViewModel()
{
List2 = new ObservableCollection<MyListItem2>();
Random rand = new Random();
for (int i = 0; i < rand.Next(1, 20); i++)
{
MyListItem2 mli = new MyListItem2();
mli.Name = "ListItem" + i;
mli.IsMarked = false;
mli.PropertyChanged += ItemChanged;
List2.Add(mli);
}
}
private void ItemChanged(object sender, PropertyChangedEventArgs e)
{
var item = sender as MyListItem2;
Console.WriteLine(string.Format("changed: {0} {1}", item.Name, item.IsMarked));
}
}
I can see two ways:
Since you are using MVVM, implement the INotifyPropertyChanged interface on the MyListItem2 class (Microsoft Reference on INotifyPropertyChanged implementation). Raise the property change event when the IsMarked value is set/changed, then wire into the PropertyChanged event handler of the item to determine when it is changed. . OR
If you have codebehidn, add a "Checked" and/or "Unchecked" event handler on the checkbox itself from the XAML. Shown below.
CheckBox Grid.Column="0" Content="Mark" IsChecked="{Binding IsMarked}"/>
Checked="IsMarked_Checked"
Codebehind
public void IsMarked_Checked(object sender, RoutedEventArgs e)
{
var ck = sender As Checkbox;
if (ck == null)
{
return;
}
// do whatever you need to here using the datacontext of the Checkbox
}
If you want to know when a check box is checked/unchecked by the user you will need to trigger on the event from the checkbox.
Use something like this:
private void MyCheckBox_Checked(object sender, RoutedEventArgs e)
{
//check IsChecked of MyCheckBox here
}
Try setting binding Mode:
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"

Check which object invoked the ValueChanged Event of a Slider

I am trying to determine which of my Sliders Invoked the Event, so I can call the OutputAnalogChannel Method with the Index of the Slider and the Slider value.
My Sliders that could potentially invoke the Event are called:
{ K8055AnalogOutputSlider1, K8055AnalogOutputSlider2, [...], K8055AnalogOutputSlidern }
So nothing is wrong with the following code, it works, but I feel like this is a very 'bad' way of solving this problem.
What i was thinking is that some kind of 'additional' integer value is added to the Slider which corresponds to the correct Slider at the Index.
Honestly this answer is probably hiding somewhere on stackoverflow, but I am not sure what I'd be searching for, so i posted here. Thanks in advance!
private void K8055AnalogOutputSliderValueChanged(object sender, RoutedEventArgs e)
{
Slider slider = sender as Slider;
K8055.OutputAnalogChannel(int.Parse(slider.Name[slider.Name.Length - 1].ToString()), (int)slider.Value);
}
You could use the controls' Tag property. Just set the property to the index of the control and then check it in your event handler:
K8055.OutputAnalogChannel((int)slider.Tag, (int)slider.Value);
This is a little more work, but it makes things incredibly easy to modify and maintain and read. It also gets you started taking advantage of some very powerful features of WPF. But if you're under severe deadline pressure, Vincent's quick fix has the virtue of simplicity.
C#
public class ChannelViewModel : INotifyPropertyChanged
{
private string _name = "";
public string Name
{
get { return _name; }
set
{
_name = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(Name)));
}
}
private int _channel = 0;
public int Channel
{
get { return _channel; }
set
{
_channel = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(Channel)));
}
}
private int _value = 0;
public int Value
{
get { return _value; }
set
{
_value = value;
K8055.OutputAnalogChannel(Channel, Value);
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(Value)));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
Channels.Add(new ChannelViewModel { Name="Fred", Channel = 1, Value = 3 });
Channels.Add(new ChannelViewModel { Name="Bob", Channel = 2, Value = 35 });
}
public ObservableCollection<ChannelViewModel> Channels { get; private set; }
= new ObservableCollection<ChannelViewModel>();
public event PropertyChangedEventHandler PropertyChanged;
}
XAML
<ItemsControl
ItemsSource="{Binding Channels}"
BorderBrush="Black"
BorderThickness="1"
>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="2">
<TextBlock>Channel
<Run Text="{Binding Channel, Mode=OneWay}" />:
<Run Text="{Binding Name, Mode=OneWay}" /></TextBlock>
<Slider Value="{Binding Value}" Minimum="1" Maximum="100" Width="300" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

WPF Bound Editable Combobox not changing ID on Text Change

I'm no sure what the right way to bind the combobox control is to my view model.
I'm using a MVVM approach so in my viewmodel i'm loading all the CDType data and am binding their source in combination with the actual record properties model.CDType.id and model.CDType.name.
What happens is that when i change the text? - i keep my old id (from the load routine) and get a new text value from the combobox binding therefor always writing over the existing record instead of creating a new one.
How can i make my combobox set the id to 0 / undefined if the text isn't in the list? (manually? - kind of lame)
Anything will help - thanks!
TL;DR: Editable Combo-box not updating ID on text change.
Sample Xaml binding:
<ComboBox x:Name="ddlCDType"
IsTextSearchEnabled="True"
IsTextSearchCaseSensitive="False"
StaysOpenOnEdit="True"
TextSearch.TextPath="Name"
ItemsSource="{Binding CDTypes}"
SelectedValue="{Binding Assignment.CDType.ID}"
Text="{Binding Assignment.CDType.Name,
UpdateSourceTrigger=PropertyChanged,
NotifyOnValidationError=True,
ValidatesOnDataErrors=True}"
DisplayMemberPath="Name"
SelectedValuePath="ID"
IsEditable="True"
HorizontalAlignment="Left"
Margin="98,10,0,0"
VerticalAlignment="Top"
Width="136" />
as I can understand you need update the previous last selected value when the name of selected ComboBox item is changed manually and this name is not presented in the ComboBox ItemSource. Here is my suggestion, it is combination of your ComboBox control data binding and logic defined in the ViewModel.
Explanation of binding
Since the ComboBox ItemsSource is the collection of Cd (the collection of ComboModels), thus the TextSearch.TextPath binding should be defined as CDType.Name where the CdType is property defined in the ComboModel that describes the sub-model, and the Name is an actual search path which is describing the sub-model.
The ComboBox Text property is binded to AssignmentText property to trigger the updating logic when the combo is lost the focus (as defined in binding).
The ItemsSource is trivial as defined in your sample code.
Selected value will bring a whole model to update (in case we change the selected value name).
Since the ComboBox ItemsSource is the collection of Cd (let's call this ComboModel) the DisplayMemberPath binding should be defined as CDType.Name where the CdType is property defined in the ComboModel that describes the sub-model, and the Name is an actual search path which is describing the sub-model.
Xaml Code:
<Grid VerticalAlignment="Bottom">
<Grid.DataContext>
<comboBoxWhenNoAnySelectedHelpAttempt:ComboboxDataContext/>
</Grid.DataContext>
<StackPanel Orientation="Vertical">
<ComboBox x:Name="ddlCDType"
IsTextSearchEnabled="True"
IsTextSearchCaseSensitive="False"
StaysOpenOnEdit="True"
TextSearch.TextPath="CDType.Name"
Text="{Binding AssignmentText,
UpdateSourceTrigger=LostFocus, Mode=TwoWay,
ValidatesOnDataErrors=True}"
ItemsSource="{Binding CDTypes}"
SelectedValue="{Binding Assignment}"
DisplayMemberPath="CDType.Name"
IsEditable="True"
HorizontalAlignment="Left"
Margin="98,10,0,0"
VerticalAlignment="Top"
Width="136" />
<!--added to see changes in updated combo box item-->
<TextBlock >
<Run Text="Name:"/>
<Run Text="{Binding Assignment.CDType.Name, UpdateSourceTrigger=PropertyChanged}"></Run>
<Run Text="Id:"/>
<Run Text="{Binding Assignment.CDType.ID, UpdateSourceTrigger=PropertyChanged}"></Run>
</TextBlock>
</StackPanel>
</Grid>
VM code
public class ComboboxDataContext:BaseObservableObject
{
private ObservableCollection<ComboModel> _cdTypes;
private ComboModel _assignment;
private string _assignmentText;
private string _lastAcceptedName;
public ComboboxDataContext()
{
CDTypes = new ObservableCollection<ComboModel>
{
new ComboModel
{
CDType = new ComboModelSubModel
{
Name = "Cd-1",
ID = "1",
},
},
new ComboModel
{
CDType = new ComboModelSubModel
{
Name = "Cd-2",
ID = "2",
}
},
new ComboModel
{
CDType = new ComboModelSubModel
{
Name = "Cd-3",
ID = "3",
},
},
new ComboModel
{
CDType = new ComboModelSubModel
{
Name = "Cd-4",
ID = "4",
}
}
};
Assignment = CDTypes.FirstOrDefault();
}
public ObservableCollection<ComboModel> CDTypes
{
get { return _cdTypes; }
set
{
_cdTypes = value;
OnPropertyChanged();
}
}
public ComboModel Assignment
{
get { return _assignment; }
set
{
if (value == null)
_lastAcceptedName = _assignment.CDType.Name;
_assignment = value;
OnPropertyChanged();
}
}
//on lost focus when edit is done will check and update the last edited value
public string AssignmentText
{
get { return _assignmentText; }
set
{
_assignmentText = value;
OnPropertyChanged();
UpDateSourceCollection(AssignmentText);
}
}
//will do the the update on combo lost focus to prevent the
//annessasary updates (each property change will make a lot of noice in combo)
private void UpDateSourceCollection(string assignmentText)
{
var existingModel = CDTypes.FirstOrDefault(model => model.CDType.Name == assignmentText);
if (existingModel != null) return;
if (_lastAcceptedName == null)
{
CDTypes.Add(new ComboModel{CDType = new ComboModelSubModel{ID = string.Empty, Name = assignmentText}});
}
else
{
var existingModelToEdit = CDTypes.FirstOrDefault(model => model.CDType.Name == _lastAcceptedName);
if(existingModelToEdit == null) return;
existingModelToEdit.CDType.Name = assignmentText;
existingModelToEdit.CDType.ID = string.Empty;
}
}
}
public class ComboModel:BaseObservableObject
{
private ComboModelSubModel _cdType;
public ComboModelSubModel CDType
{
get { return _cdType; }
set
{
_cdType = value;
OnPropertyChanged();
}
}
}
public class ComboModelSubModel:BaseObservableObject
{
private string _id;
private string _name;
public string ID
{
get { return _id; }
set
{
_id = value;
OnPropertyChanged();
}
}
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged();
}
}
}
BaseObservableObject code
/// <summary>`enter code here`
/// implements the INotifyPropertyChanged (.net 4.5)
/// </summary>
public class BaseObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged<T>(Expression<Func<T>> raiser)
{
var propName = ((MemberExpression)raiser.Body).Member.Name;
OnPropertyChanged(propName);
}
protected bool Set<T>(ref T field, T value, [CallerMemberName] string name = null)
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
OnPropertyChanged(name);
return true;
}
return false;
}
}
Regards.

binding groupboxes to may stackpanel

I want to bind Groupboxes to my stackpanel.
c#:
internal ObservableCollection<GroupBox> BindingTest
{
get
{
ObservableCollection<GroupBox> collection = new ObservableCollection<GroupBox>();
for (int counter = 0; counter < 5; counter++)
{
GroupBox groupBox = new GroupBox();
groupBox.Header = " ... Header ... ";
groupBox.Content = " ... Content ... ";
collection.Add(groupBox);
}
return collection;
}
}
and the xaml code:
<StackPanel x:Name="Dynamic" Grid.Row="1" Margin="29,118,6,0">
<ItemsControl ItemsSource="{Binding Path=BindingTest}" Height="482">
<ItemsControl.ItemTemplate>
<DataTemplate>
<GroupBox>
<GroupBox.Header>{Binding Path=Header}</GroupBox.Header>
<GroupBox.Content>{Binding Path=Content}</GroupBox.Content>
</GroupBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
where does my error in reasoning?
In WPF Binding, you usually don't bind to Controls, you just bind to objects and let the Markup handle UI details. Ideally your objects should implement INotifyPropertyChanged to notify the Binding of property changes. Control creation is handled already by the DataTemplate, it already creates a GroupBox for you and binds its properties to the properties of the object used in the binding.
Your ViewModel should, if possible, not know anything about the UI, or use any Controls. The MVVM pattern is meant to decouple UI and Data/Logic.
The class of your data objects could look something like this:
public class DataObject : INotifyPropertyChanged
{
private string header;
private string content;
public string Header {
get { return header; }
set { header = value; RaisePropertyChanged("Header"); }
}
public string Content {
get { return content; }
set { content= value; RaisePropertyChanged("Content"); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName) {
var handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The property you bind to would also look different:
internal ObservableCollection<DataObject> BindingTest
{
get
{
ObservableCollection<DataObject> collection = new ObservableCollection<DataObject>();
for (int counter = 0; counter < 5; counter++)
{
DataObject item = new DataObject ();
item.Header = " ... Header ... ";
item.Content = " ... Content ... ";
collection.Add(item );
}
return collection;
}
}
I assume creating a new Collection every time the property is accessed is just for testing purposes.
Just change the BindingTest property into public field.. .It will automatically bind the data to the UI...
Basically xaml allows you to bind the public properties to the UI as we have limitation over the other fields like internal and private..
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
public ObservableCollection<GroupBox> BindingTest
{
get
{
ObservableCollection<GroupBox> collection = new ObservableCollection<GroupBox>();
for (int counter = 0; counter < 5; counter++)
{
GroupBox groupBox = new GroupBox();
groupBox.Header = " ... Header ... ";
groupBox.Content = " ... Content ... ";
collection.Add(groupBox);
}
return collection;
}
}
}
xaml code as follows
<Grid >
<StackPanel x:Name="Dynamic" >
<ItemsControl ItemsSource="{Binding BindingTest, RelativeSource={RelativeSource AncestorType={x:Type this:MainWindow}}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<GroupBox Header="{Binding Path=Header}"
Content="{Binding Path=Content}">
</GroupBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>

Categories

Resources