I have combo box with some contents. I want it to get reset to the first item when a button is clicked.
WPF and C#.net is used.
<ComboBox x:Name="categoriesComboBox" Height="40" Grid.Row="0" ItemsSource="{Binding Categories}" SelectedValue="{Binding SelectedCategory}"
VerticalContentAlignment="Center" Background="{StaticResource InventoryManagementAlternatingRowBackground}" BorderBrush="Transparent">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding CategoryName}" FontSize="20" Margin="3,0" FontFamily="Malgun Gothic" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
private void ClearFilter_Click(object sender, RoutedEventArgs e)
{
SearchFiltersControl s = new SearchFiltersControl();
s.ResetCategoryComboBox();
}
public void ResetCategoryComboBox()
{
categoriesComboBox.SelectedIndex = -1;
}
I am adding the ViewModel. Add I want to how to use this View Model to reset the ComboBox:
using System;
using System.Collections.ObjectModel;
namespace NextGen.Optik.UI.Presentation.ViewModels.Inventory.Contracts
{
public class SearchFilterCategoryViewModel : ViewModelBase
{
private int _categoryId;
public int CategoryId
{
get => _categoryId;
set
{
if (_categoryId != value)
{
_categoryId = value;
RaisePropertyChangedEvent();
}
}
}
private string _categoryName;
public string CategoryName
{
get => _categoryName;
set
{
if (_categoryName != value)
{
_categoryName = value;
RaisePropertyChangedEvent();
}
}
}
private ObservableCollection<SearchFilterViewModel> _filters = new ObservableCollection<SearchFilterViewModel>();
public ObservableCollection<SearchFilterViewModel> Filters
{
get => _filters;
set
{
if (_filters != value)
{
_filters = value;
RaisePropertyChangedEvent();
}
}
}
}
}
Expected Result: When the reset button is clicked, first item has to be selected.
Actual Results: Not getting reset.
Related
My scenario: I have a usercontrol consisting of a comboBox, and a TextBox. The comboBox should hold numbers contained in an ObservableCollection.
The task: The numbers in the ObservableCollection represent paths to book-chapters; therefore each chapter is unique. Meaning: if I have chapters 1 - 5, then the first userControl combo should show all chapters 1-5 (whereas one of them is selected randomly), the second userControl combo contains all chapters, but not the one selected in the previous combo, and so on. The textBox is for annotations for the chapters.
What I achieved so far: I have currently no model; just a main viewModel (ItemsViewModel in my case), and a viewModel for my userControl (PathViewModel). Then there is the mainWindow view.
The problem: On my mainWindow I can create several dynamically created userControls. The userControl TextBox is currently bound to a text property, while the index of the comboBox is bound to another property. But I don't know:
- how to gain access to the index, selected item/value of the specifically userControls
- how to react to a comboBox item/index change
Here is my code:
The userControl
<UserControl> <StackPanel Orientation="Horizontal">
<ComboBox x:Name="combo" Margin="10" MinWidth="60" VerticalAlignment="Center" ItemsSource="{Binding AvailableNumbers}" SelectedIndex="{Binding TheIndex}" />
<TextBox Margin="10" MinWidth="120" Text="{Binding TheText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
The MainWindow
<Window>...<Window.DataContext>
<local:ItemsViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel x:Name="HostPanel">
<ItemsControl ItemsSource="{Binding PathViewModels}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:PathControl/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
<StackPanel Grid.Column="1">
<Button Command="{Binding UCCreationCommand}" Content="Add User Control" Margin="10"/>
<Button Command="{Binding UCDeletionCommand}" CommandParameter="" Content="Delete User Control" Margin="10"/>
<Button Command="{Binding ReadoutCommand}" Content="Show ITEMS" Margin="10"/>
</StackPanel>
</Grid></Window>
My main ViewModel (called ItemsViewModel)
public class ItemsViewModel : NotifyPropertyChangedBase
{private int _aNumber;
public int ANumber
{
get { return _aNumber; }
set { _aNumber = value;
OnPropertyChanged(ref _aNumber, value);
}
}
public ObservableCollection<PathViewModel> PathViewModels { get; set; } = new
ObservableCollection<PathViewModel>();
public ObservableCollection<int> AllNumbers { get; set; } = new ObservableCollection<int>();
public ItemsViewModel()
{
UCCreationCommand = new CommandDelegateBase(UCCreationExecute, UCCreationCanExecute);
UCDeletionCommand = new CommandDelegateBase(UCDeletionExecute, UCDeletionCanExecute);
ReadoutCommand = new CommandDelegateBase(ReadoutExecute, ReadoutCanExecute);
AllNumbers.Add(1);
AllNumbers.Add(2);
AllNumbers.Add(3);
AllNumbers.Add(4);
AllNumbers.Add(5);
}
private bool ReadoutCanExecute(object paramerter)
{
if (PathViewModels.Count > 0)
{
return true;
}
return false;
}
private void ReadoutExecute(object parameter)
{
//just for testing
}
public ICommand UCCreationCommand { get; set; }
public ICommand UCDeletionCommand { get; set; }
public ICommand ReadoutCommand { get; set; }
private bool UCCreationCanExecute(object paramerter)
{
if (PathViewModels.Count < 8)
{
return true;
}
else
{
return false;
}
}
private void UCCreationExecute(object parameter)
{
PathViewModel p = new PathViewModel();
foreach (int i in AllNumbers)
{
p.AvailableNumbers.Add(i);
}
int rndIndex = 0;
Random rnd = new Random();
//creates a random chapter index
rndIndex = rnd.Next(0, p.AvailableNumbers.Count);
//just explicit for debugging reasons
p.TheIndex = rndIndex;
AllNumbers.RemoveAt(rndIndex);
PathViewModels.Add(p);
}
private bool UCDeletionCanExecute(object paramerter)
{
if (PathViewModels.Count != 0)
{
return true;
}
else
{
return false;
}
}
private void UCDeletionExecute(object parameter)
{
PathViewModel p = new PathViewModel();
int delIndex = PathViewModels.Count - 1;
p = PathViewModels[delIndex];
AllNumbers.Add((int)p.TheValue+1);
PathViewModels.Remove(p);
}
}
And finally my UserControl ViewModel:
public class PathViewModel : NotifyPropertyChangedBase
{
public ObservableCollection<int> AvailableNumbers { get; set; } = new ObservableCollection<int>();
private int _theIndex;
public int TheIndex
{
get { return _theIndex; }
set
{
_theIndex = value;
OnPropertyChanged(ref _theIndex, value);
}
}
private int _theValue;
public int TheValue
{
get { return _theValue; }
set
{
_theValue = value;
OnPropertyChanged(ref _theValue, value);
}
}
private string _theText;
public string TheText
{
get { return _theText; }
set
{
_theText = value;
OnPropertyChanged(ref _theText, value);
}
}
public PathViewModel()
{
}
}
Any hints on how to go on from here would be highly appreaciated.
I am trying to have a WPF window with a ComboBox, with which you select an item, and then use TextBoxes below to edit the properties of the currently selected item, eg Name and Age.
How do I do this with Data Binding, ie. to bind the name TextBox to the Name property of the currently selected item in the ComboBox?
My XAML is
<Window x:Class="BindingTest001.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
>
<Grid>
<TextBox HorizontalAlignment="Left" Height="23" Margin="10,75,0,0" TextWrapping="Wrap" Text="{Binding Path=CURR.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="497"/>
<TextBox HorizontalAlignment="Left" Height="23" Margin="10,103,0,0" TextWrapping="Wrap" Text="{Binding Path=CURR.Age, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="120"/>
<ComboBox ItemsSource="{Binding Path=MO}" SelectedValue="{Binding Path=CURR, Mode=TwoWay}" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="497" Name="MyComboBox"/>
<Button Content="Button" HorizontalAlignment="Left" Margin="322,181,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
</Grid>
</Window>
Where my code behind is
public partial class MainWindow : Window
{
ObservableCollection<myObj> mo;
public MainWindow()
{
InitializeComponent();
mo = new ObservableCollection<myObj>();
mo.Add(new myObj("Test1", 2));
var ct = new MainWindowDatacontext(this);
this.DataContext = ct;
this.MyComboBox.SelectedIndex = 0;
}
private class MainWindowDatacontext : INotifyPropertyChanged
{
MainWindow parent;
myObj curr;
public MainWindowDatacontext(MainWindow mainWindow)
{
this.parent = mainWindow;
}
public ObservableCollection<myObj> MO
{
get
{
return parent.mo;
}
}
public myObj CURR
{
get
{
return curr;
}
set
{
this.curr = value;
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void executePropertyChanged(string s)
{
if(PropertyChanged!=null)
{
PropertyChanged(this.parent, new PropertyChangedEventArgs(s));
}
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
mo.Add(new myObj("Test2", 10));
}
}
But the Data Binding only works for the ComboBox- the TextBoxes never bind to anything.
myObj is very simple:
public class myObj : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void propertyChanged(string s)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(s));
}
}
string name;
public string Name
{
get { return name; }
set {
name = value;
propertyChanged("Name");
}
}
int age;
public myObj(string p1, int p2)
{
this.name = p1;
this.age = p2;
}
public int Age
{
get { return age; }
set { age = value;
propertyChanged("Age");
}
}
public override string ToString()
{
return String.Format("{0}, {1}", name, age);
}
}
Because you want to bind to the property of another element in your application you should use Binding.ElementName Property. So Change your TextBox's Binding like this:
Text="{Binding SelectedItem.Name, ElementName=MyComboBox,
Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
Text="{Binding SelectedItem.Age, ElementName=MyComboBox,
Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
You are using SelectedValue in your ComboBox rather than SelectedItem.
The former is for when you want to select one property out of the object in the ComboBox's ItemsSource rather than the object itself. It's used in conjunction with the SelectedValuePath.
As you want to edit more than one property of the selected item you need to use SelectedItem.
I am new-bee at WPF, i am trying to populate my combox control which is there within my listbox
XAML :
<Window.Resources>
<DataTemplate x:Key="UserTemplate" >
<StackPanel Orientation="Horizontal" >
<ComboBox Name="rule" ItemsSource="{Binding}" DisplayMemberPath="DataContext.RuleType" Width="85" Height="20"
SelectedValuePath="DataContext.RuleType" SelectedValue="{Binding Path=DataContext.RuleType}"/>
<TextBlock Text="{Binding Path= Name1}" Width="85" Margin="5,5,5,5"></TextBlock>
<Button Content="Delete" Click="cmdDeleteUser_Clicked" Margin="5,5,5,5" />
<Button Content="Add" Click="cmdAddUser_Clicked" Margin="5,5,5,5" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox Name="lbUsers" ItemsSource="{Binding }" ItemTemplate="{StaticResource UserTemplate}"/>
</Grid>
CODE BEHIND:
public ObservableCollection<User> Users;
ObservableCollection<Listdata> listeddata;
ObservableCollection<Records> Record;
public MainWindow()
{
InitializeComponent();
Users = new ObservableCollection<User>() {
new User() { Name = "", Age = "" },
};
DataboundListbox.Records record = new Records();
RuleType = record.record_Rule();
lbUsers.DataContext = Users;
}
private string _Name;
public string Name1
{
get { return _Name; }
set
{
if (value != _Name)
{
_Name = "John";
NotifyPropertyChanged("Name");
}
}
}
private List<string> _RuleType;
public List<string> RuleType
{
get { return _RuleType; }
set
{
if (value != _RuleType)
{
_RuleType = value;
NotifyPropertyChanged("RuleType");
}
}
}
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
private void cmdDeleteUser_Clicked(object sender, RoutedEventArgs e)
{
Button cmd = (Button)sender;
if (cmd.DataContext is User)
{
User deleteme = (User)cmd.DataContext;
Users.Remove(deleteme);
}
}
private void cmdAddUser_Clicked(object sender, RoutedEventArgs e)
{
Button cmd = (Button)sender;
if (cmd.DataContext is User)
{
var addedUser = new User() { Name = "", Age = "" };
Users.Add(addedUser);
}
}
private List<string> _prp;
public List<string> prp
{
get { return _prp; }
set
{
if (value != _prp)
{
_RuleType = value;
NotifyPropertyChanged("prp");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
Before I can answer your question there are some confusions that should be cleared up.
If User has already a member named Name then what's Name1 in parent class for?
If RuleType is a list, how come it's set as the SelectedValue of your ComboBox, Shouldn't it be ComboBox.itemsSource instead? If it should, then where is the property defined to keep the ComboBox.SelectedValue?
How come there is an Add button inside the UserTemplate? Delete button is ok but i think Add belongs outside of the ListBox.
If i understand your issue correctly, then this is the solution I can think of.
Fisrt: User needs a property like SelectedRule to keep Combobox.SelectedItem:
public class User : INotifyPropertyChanged
{
// implementation of INotifyPropertyChanged
string _name;
public string Name
{
get
{
return _name;
}
set
{
_name = value;
NotifyPropertyChanged("Name");
}
}
int _age;
public int Age
{
get
{
return _age;
}
set
{
_age = value;
NotifyPropertyChanged("Age");
}
}
string _selectedRule;
public string SelectedRule
{
get
{
return _selectedRule;
}
set
{
_selectedRule = value;
NotifyPropertyChanged("SelectedRule");
}
}
}
Second: Your DataTemplate should change like this:
<Window.Resources>
<DataTemplate x:Key="UserTemplate" >
<StackPanel Orientation="Horizontal" >
<ComboBox Name="rule" ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=RuleType}" DisplayMemberPath="." Width="85" Height="20"
SelectedItem="{Binding SelectedRule}"/>
<TextBlock Text="{Binding Path= Name}" Width="85" Margin="5,5,5,5"></TextBlock>
<Button Content="Delete" Click="cmdDeleteUser_Clicked" Margin="5,5,5,5" />
</StackPanel>
</DataTemplate>
</Window.Resources>
Finally the ListBox part changes as below:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListBox Grid.Row="0" Name="lbUsers" ItemsSource="{Binding}" ItemTemplate="{StaticResource UserTemplate}"/>
<Button Grid.Row="1" Content="Add" Click="cmdAddUser_Clicked" Margin="5,5,5,5" />
</Grid>
If you're gonna bring Add button out like the above code, then you should remove if (cmd.DataContext is User) from cmdAddUser_Clicked method.
Problem :
The main problem is on this two line:
{Binding Path=DataContext.RuleType}
{Binding Path= Name1}
Since you already declare your dataContext, DataContext.RuleType will causes the compiler to search for yourdatacontext.DataContext.RuleType which is obviously not the thing you want.
lbUsers.DataContext = Users;
Your data context is a collection of User class and does not contain Name1. Thus Binding Path=Name1 will return "property not found" error
Solution
In WPF, MVVM ( model view viewmodel) pattern is highly encouraged. One of its main feature is it seperate GUI logic from Business Logic, making the code cleaner and easier to maintain.
Step 1: Create a ViewModel
public class UserViewModel:INotifyPropertyChanged
{
private string name;
private string age;
private string rule;
private List<string> ruleType;
public String Name
{
get { return name; }
set { name = value; NotifyPropertyChanged("Name"); }
}
public String Age
{
get { return age; }
set { age = value; NotifyPropertyChanged("Age"); }
}
public String Rule
{
get { return rule; }
set { rule = value; NotifyPropertyChanged("Rule"); }
}
public List<string> RuleType
{
get { return ruleType; }
set { ruleType = value; NotifyPropertyChanged("RuleType"); }
}
public UserViewModel()
{
name = "name";
age = "";
ruleType = new List<string>();
}
#region NotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
#endregion
}
}
Step 2 : Link your data context to the viewmodel
public MainWindow()
{
InitializeComponent();
Users = new ObservableCollection<UserViewModel>();
//setup your data here
//example:
UserViewModel userViewModel = new UserViewModel();
//populate your combobox here
userViewModel.RuleType.Add("rule1")
userViewModel.RuleType.Add("rule2");
userViewModel.RuleType.Add("rule3");
Users.Add(new UserViewModel());
lbUsers.DataContext = Users ;
}
Step 3 : Update your xaml
<Window.Resources>
<DataTemplate x:Key="UserTemplate" >
<StackPanel Orientation="Horizontal" >
<ComboBox Name="rule" ItemsSource="{Binding RuleType}" Width="85" Height="20"
SelectedValue="{Binding Rule}"/>
<TextBlock Text="{Binding Path= Name}" Width="85" Margin="5,5,5,5"></TextBlock>
<Button Content="Delete" Click="cmdDeleteUser_Clicked" Margin="5,5,5,5" />
<Button Content="Add" Click="cmdAddUser_Clicked" Margin="5,5,5,5" />
</StackPanel>
</DataTemplate>
</Window.Resources>
When i am typing, bahman already post a quite detailed answer.So i stopped here. If you require any explaination or solution from me just asked will do.
In future if you suspect any error regarding binding, you can search your output window.
If you see your output window you possibly will found this
System.Windows.Data Error: 40 : BindingExpression path error: 'DataContext' property not found on 'object' ''User' (HashCode=9080996)'. BindingExpression:Path=DataContext.RuleType; DataItem='User' (HashCode=9080996); target element is 'ComboBox' (Name=''); target property is 'SelectedValue' (type 'Object')
System.Windows.Data Error: 40 : BindingExpression path error: 'Name1' property not found on 'object' ''User' (HashCode=9080996)'. BindingExpression:Path=Name1; DataItem='User' (HashCode=9080996); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
I created a new TextBlock class which has ItemsSource property and translates that ItemsSource into "Run" object:
public class MultiTypeDynamicTextBlock : TextBlock
{
public interface ISection
{
Inline GetDisplayElement();
}
public class TextOption : ISection
{
private Run mText;
public TextOption(string aText)
{
mText = new Run();
mText.Text = aText.Replace("\\n", "\n");
}
public Inline GetDisplayElement()
{
return mText;
}
}
public class LineBreakOption : ISection
{
public Inline GetDisplayElement()
{
return new LineBreak();
}
public ISection Clone()
{
return new LineBreakOption();
}
}
public class ImageOption : ISection
{
private InlineUIContainer mContainer;
public ImageOption(string aDisplay)
{
Image lImage;
lImage = new Image();
lImage.Source = new BitmapImage(new Uri(Environment.CurrentDirectory + aDisplay));
lImage.Height = 15;
lImage.Width = 15;
mContainer = new InlineUIContainer(lImage);
}
public Inline GetDisplayElement()
{
return mContainer;
}
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(ObservableCollection<ISection>), typeof(MultiTypeDynamicTextBlock),
new UIPropertyMetadata(new ObservableCollection<ISection>(),
new PropertyChangedCallback(SetContent)));
public ObservableCollection<ISection> ItemsSource
{
get
{
return GetValue(ItemsSourceProperty) as ObservableCollection<ISection>;
}
set
{
if (ItemsSource != null)
ItemsSource.CollectionChanged -= CollectionChanged;
SetValue(ItemsSourceProperty, value);
SetContent();
ItemsSource.CollectionChanged += CollectionChanged;
}
}
private void CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
SetContent();
}
private static void SetContent(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DependencyObject lParent = d;
MultiTypeDynamicTextBlock lPanel = lParent as MultiTypeDynamicTextBlock;
if (lPanel != null)
{
lPanel.ItemsSource = e.NewValue as ObservableCollection<ISection>;
}
}
private void SetContent()
{
if (ItemsSource != null)
{
Inlines.Clear();
foreach (ISection lCurr in ItemsSource)
{
Inlines.Add(lCurr.GetDisplayElement());
}
}
}
If I Bind the ItemsSource directly to the DataContext, it works.
But if I bind it to an object that changes at runtime (such as SelectedItem on a ListBox) it doesn't update the text when a new item is selected.
<StackPanel>
<ListBox x:Name="TheList" ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel DataContext="{Binding ElementName=TheList, Path=SelectedItem}">
<TextBlock Text="{Binding Title}" FontSize="20"/>
<local:MultiTypeDynamicTextBlock ItemsSource="{Binding Items}"/>
</StackPanel>
</StackPanel>
Any reason why?
In your example, does the SelectedItem has two properties Title and Items? Or is Items a property in your viewmodel? If the answer is the latter, than you can find a solution below.
I don't entirely understand what you mean, but I'll give it a try.
If you mean that the ItemsSource on your custom control isn't set, than you have to point XAML into the right direction.
Below you can find a solution, if this is what you want to achieve.
What I did is pointing the compiler to the right source with this line of code:
ItemsSource="{Binding DataContext.Items, RelativeSource={RelativeSource AncestorType=Window}}"
Here you say that the compiler can find the Binding property in the DataContext of the Window (or any control where you can find the property).
<StackPanel>
<ListBox x:Name="TheList" ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel DataContext="{Binding ElementName=TheList, Path=SelectedItem}">
<TextBlock Text="{Binding Title}" FontSize="20"/>
<local:MultiTypeDynamicTextBlock ItemsSource="{Binding DataContext.Items, RelativeSource={RelativeSource AncestorType=Window}}"/>
</StackPanel>
</StackPanel>
Hopefully this helped.
EDIT
The title property will changes when I select another one from the ListBox.
If Items is set to a new ObservableCollection, do you call the OnPropertyChanged event for Items when the SelectedItem changes?
OnPropertyChanged("Items");
Thank you for your help.
I managed to fix this by updating the MultiTypeDynamicTextBlock as follows:
public class MultiTypeDynamicTextBlock : TextBlock
{
public interface ISection
{
Inline GetDisplayElement();
ISection Clone();
}
public class TextOption : ISection
{
private Run mText;
public TextOption(string aText)
{
mText = new Run();
mText.Text = aText.Replace("\\n", "\n");
}
public Inline GetDisplayElement()
{
return mText;
}
public ISection Clone()
{
return new TextOption(mText.Text);
}
}
public class LineBreakOption : ISection
{
public Inline GetDisplayElement()
{
return new LineBreak();
}
public ISection Clone()
{
return new LineBreakOption();
}
}
public class SectionList
{
private ObservableCollection<ISection> mList;
public Action CollectionChanged;
public ObservableCollection<ISection> Items
{
get
{
ObservableCollection<ISection> lRet = new ObservableCollection<ISection>();
foreach (ISection lCurr in mList)
{
lRet.Add(lCurr.Clone());
}
return lRet;
}
}
public int Count { get { return mList.Count; } }
public SectionList()
{
mList = new ObservableCollection<ISection>();
}
public void Add(ISection aValue)
{
mList.Add(aValue);
}
public SectionList Clone()
{
SectionList lRet = new SectionList();
lRet.mList = Items;
return lRet;
}
}
public MultiTypeDynamicTextBlock()
{
}
public static readonly DependencyProperty ItemsCollectionProperty =
DependencyProperty.Register("ItemsCollection", typeof(SectionList), typeof(MultiTypeDynamicTextBlock),
new UIPropertyMetadata((PropertyChangedCallback)((sender, args) =>
{
MultiTypeDynamicTextBlock textBlock = sender as MultiTypeDynamicTextBlock;
SectionList inlines = args.NewValue as SectionList;
if (textBlock != null)
{
if ((inlines != null) && (inlines.Count > 0))
{
textBlock.ItemsCollection.CollectionChanged += textBlock.ResetInlines;
textBlock.Inlines.Clear();
foreach (ISection lCurr in textBlock.ItemsCollection.Items)
{
textBlock.Inlines.Add(lCurr.GetDisplayElement());
}
}
else
{
inlines = new SectionList();
inlines.Add(new TextOption("No value set"));
textBlock.ItemsCollection = inlines;
}
}
})));
public SectionList ItemsCollection
{
get
{
return (SectionList)GetValue(ItemsCollectionProperty);
}
set
{
SectionList lTemp;
if (value == null)
{
lTemp = new SectionList();
lTemp.Add(new TextOption("No value set for property"));
}
else
{
lTemp = value;
}
SetValue(ItemsCollectionProperty, lTemp);
}
}
private void ResetInlines()
{
Inlines.Clear();
foreach (ISection lCurr in ItemsCollection.Items)
{
Inlines.Add(lCurr.GetDisplayElement());
}
}
}
And I update the fields that were Binded to be of type MultiTypeDynamicTextBlock.SectionList
As long as I am using a copy (Clone) it is working, for some reason when I don't clone it removes the value from the display in the list, if someone knows why I would love to learn but I managed to go around it.
the XAML of the window is:
<StackPanel>
<ListBox x:Name="TheList" ItemsSource="{Binding GeneralItems}" SelectedItem="{Binding SelectedItem}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Title}" FontSize="20"/>
<local:MultiTypeDynamicTextBlock ItemsCollection="{Binding Items}" Margin="20,0,0,0"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel DataContext="{Binding GeneralItems, Path=SelectedItem}">
<TextBlock Text="{Binding Title}" FontSize="20"/>
<local:MultiTypeDynamicTextBlock DataContext="{Binding Items}" ItemsCollection="{Binding}" Margin="20,0,0,0"/>
</StackPanel>
</StackPanel>
Hello I am new to WPF and I am not sure how to do the data binding and the code behind to get my textbox and button to be enabled and disabled.
If you could show me how to get it to work in the example below it would help me out in my project.
XAML
<ComboBox Name="ComboBoxA"
Margin="5"
SelectedIndex="0" SelectionChanged="ComboBoxA_SelectionChanged" >
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" Height="Auto" Margin="5" />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
<ComboBoxItem Content="Option1" Width="72" />
<ComboBoxItem Content="Option2" Width="72" />
<ComboBoxItem Content="Option3" Width="72" />
</ComboBox>
<TextBox Name="TextBoxA"
Margin="5"
Width="200"
IsEnabled="{Binding TextBoxEnabled}" />
<Button Name="ButtonA"
Content="Next"
HorizontalAlignment="left"
Margin="5"
IsEnabled="{Binding ButtonEnabled} />
C#
private void ComboBoxA_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
TextBoxA = new TextBox();
ButtonA = new Button();
if (ComboBoxAfterProcessing.SelectedIndex == 0)
{
TextBoxA.IsEnabled = false;
ButtonA.IsEnabled = false;
}
else if (ComboBoxAfterProcessing.SelectedIndex == 1)
{
TextBoxA.IsEnabled = true;
ButtonA.IsEnabled = true;
}
else if (ComboBoxAfterProcessing.SelectedIndex == 2)
{
TextBoxA.IsEnabled = true;
ButtonA.IsEnabled = true;
}
}
Write a class as follows which shall look like below
public class ViewMole:INotifyPropertyChanged
{
public ViewMole()
{
ListValues = new List<string>() { "Option1", "Option2", "Option3", "Option4",
"Option5" };
}
public ICommand Click
{
get
{
return new RelayCommand();
}
}
public List<string> ListValues
{
get;
set;
}
string a;
public string A
{
get
{
return a;
}
set
{
a = value;
RaisePropertyChanged("A");
}
}
int index=0;
public int SelectedIndex
{
get
{
return index;
}
set
{
index = value;
RaisePropertyChanged("SelectedIndex");
A = ListValues[index];
if (index == 0)
{
IsEnabled = false;
}
else
{
IsEnabled = true;
}
}
}
bool isEnabled;
public bool IsEnabled
{
get
{
return isEnabled;
}
set
{
isEnabled = value;
RaisePropertyChanged("IsEnabled");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Write another class which implements ICommand as below. This is to handel button click events
public class RelayCommand : ICommand
{
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
MessageBox.Show("Button cliked");
}
}
Change your Xaml below
<ComboBox Name="ComboBoxA" SelectedIndex="{Binding SelectedIndex, UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding ListValues}"/>
<TextBox Name="TextBoxA" Margin="5" Width="200" Text="{Binding A}" IsEnabled="{Binding IsEnabled}"/>
<Button Name="ButtonA" Content="Next" HorizontalAlignment="left" Margin="5" Command="{Binding Click}" IsEnabled="{Binding IsEnabled}"/>
In Xmal.cs set the data Context as beolw
public MainWindow()
{
InitializeComponent();
DataContext = new ViewMole();
}
Refer the below links to understand why INotifyPropertyChanged and ICommand has to be used
http://wpftutorial.net/INotifyPropertyChanged.html
http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.aspx
http://msdn.microsoft.com/en-us/library/system.windows.input.icommand.aspx
you can binding your textbox and button's IsEnabe Property to the Combox's SelectedIndex
Property and you neednot response to ComboBoxA_SelectionChanged Event. in order to let the
SelectedIndex change your button and textBox's IsEnabe you need a convetor in your binding
for example: