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:
Related
i have a few checkboxes an I want funcionality to check or uncheck all checkbox. Here is a class .cs and xaml code. How to add funcionality for check or uncheck all?
public static event PropertyChangedEventHandler IsCheckedChanged;
private bool isChecked;
public WorkStep(string name)
{
Name = name;
IsChecked = true;
}
public WorkStep(string name, bool isChecked)
{
Name = name;
IsChecked = isChecked;
}
public string Name
{
get;
}
public bool IsChecked
{
get { return isChecked; }
set
{
isChecked = value;
OnIsCheckedChanged();
}
}
private void OnIsCheckedChanged()
{
PropertyChangedEventHandler handler = IsCheckedChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs("IsChecked"));
}
and xaml:
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
I would use the INotifyPropertyChanged interface like shown in the class below:
public class myClass : INotifyPropertyChanged
{
private bool _IsChecked;
public bool IsChecked
{
get { return _IsChecked; }
set
{
_IsChecked = value;
OnPropertyChanged("IsChecked");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
Then I would bind the property IsChecked to all of my checkboxes like shown below in the xaml:
<Grid>
<CheckBox x:Name="checkBox1" IsChecked="{Binding IsChecked,UpdateSourceTrigger=PropertyChanged}" Content="CheckBox" HorizontalAlignment="Left" Margin="116,90,0,0" VerticalAlignment="Top"/>
<CheckBox x:Name="checkBox2" IsChecked="{Binding IsChecked,UpdateSourceTrigger=PropertyChanged}" Content="CheckBox" HorizontalAlignment="Left" Margin="116,126,0,0" VerticalAlignment="Top"/>
<CheckBox x:Name="checkBox3" IsChecked="{Binding IsChecked,UpdateSourceTrigger=PropertyChanged}" Content="CheckBox" HorizontalAlignment="Left" Margin="116,164,0,0" VerticalAlignment="Top"/>
<Button x:Name="button" Content="Button" HorizontalAlignment="Left" Margin="380,235,0,0" VerticalAlignment="Top" Width="75" Click="button_Click"/>
</Grid>
In the MainWindow class make a new instance of the class where the IsChecked property is located (in my case myClass MyClass = new myClass()). Then add the newly created instance MyClass to the DataContext of your MainWindow (DataContext = MyClass;). I use a button control in this example to check and uncheck all of my checkboxes. If the value of the property IsChecked is true all of the checkboxes are checked and if it's false all of the checkboxes are unchecked. The MainWindow class is shown below :
public MainWindow()
{
InitializeComponent();
DataContext = MyClass;
}
myClass MyClass = new myClass();
private void button_Click(object sender, RoutedEventArgs e)
{
if(MyClass.IsChecked)
MyClass.IsChecked = false;
else
MyClass.IsChecked = true;
}
I tried to bind a property to a nested object, but it fails.
I have taken a look at those questions, but i think i made another mistake somewhere else. Maybe someone can give i hind.
WPF: How to bind to a nested property?
binding to a property of an object
To upper slider/textbox has a correct binding while the lower one fails to do so.
I have two sliders with corrosponding textboxes:
<StackPanel>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBox Text="{Binding Path= boundnumber, Mode=TwoWay, FallbackValue='binding failed'}" ></TextBox>
<Slider Value="{Binding Path= boundnumber, Mode=TwoWay}" Width="500" Maximum="1000" ></Slider>
</StackPanel>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" DataContext="{Binding Path=myDatarow}">
<TextBox Text="{Binding Path= boundnumber, Mode=TwoWay, FallbackValue='binding failed'}" ></TextBox>
<Slider Value="{Binding Path= boundnumber, Mode=TwoWay}" Width="500" Maximum="1000" ></Slider>
</StackPanel>
</StackPanel>
Code behind:
public partial class MainWindow : INotifyPropertyChanged
{
public MainWindow()
{
DataContext = this;
InitializeComponent();
}
private int _boundnumber;
public int boundnumber
{
get { return _boundnumber; }
set
{
if (value != _boundnumber)
{
_boundnumber = value;
OnPropertyChanged();
}
}
}
Datarow myDatarow = new Datarow(11);
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyname = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname));
}
}
class Datarow : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyname = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname));
}
public Datarow()
{
}
public Datarow(int number)
{
boundnumber = number;
}
private int _boundnumber;
public int boundnumber
{
get { return _boundnumber; }
set
{
if (value != _boundnumber)
{
_boundnumber = value;
OnPropertyChanged();
}
}
}
}
You need to expose your myDatarow into a public property like your boundnumber.
private DataRow _myDatarow = new DataRow(11);
public DataRow myDataRow
{
get { return _myDatarow; }
}
And just an additional advice.
It's better to separate your DataContext class from the MainWindow.
I have my xaml structure something like this:
Radio buttons and corresponding controls that will be enabled if the radio button is selected. For Example, if the radio button food is selected, then two text boxes will be enabled, for the radio button Drinks, a combo box will be enabled.
And a Button to process the the data in the dialog.
I want to enable the Button only when one radio button is selected and its corresponding controls are populated.
I am using a command for the button in my view model. Please help me do this.
Command for the button
EnableProcessButton = new RelayCommand(ExecuteButton, CanExecuteButton);
public ICommand EnableProcessButton
{
get;
internal set;
}
private bool CanExecuteButton(object obj)
{
switch (SelectedMode)
{
case "Drinks": return (!string.IsNullOrEmpty(SelectedDrink));
break;
case "Food": return ((!string.IsNullOrEmpty(text1)) && (text2 != default(int)));//The second text box is implemented as a numeric one
break;
default: return false;
break;
}
}
private void ExecuteButton(object obj)
{
//DO the required things.
}
Xaml
<StackPanel>
<TextBox x:Name="FoodBox1" Width="120" Margin="10" HorizontalAlignment="Center" VerticalAlignment="Center" IsEnabled="{Binding ElementName=Food, Path=IsChecked}" UndoLimit="5"
Text="{Binding textbox1, UpdateSourceTrigger=PropertyChanged}" TextChanged="Foodbox1_TextChanged"/>
<TextBox Name="FoodBox2" HorizontalAlignment="Center" VerticalAlignment="Center" Width="120" Margin="10" IsEnabled="{Binding ElementName=Food, Path=IsChecked}" UndoLimit="5"
MaxLength="5" TextChanged="FoodBox2_TextChanged" Validation.ErrorTemplate="{x:Null}" Text="{Binding textbox2, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
<ComboBox Name="DrinkOptions" Grid.Row="4" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" Width="120" Margin="10" IsEnabled="{Binding ElementName=Drinks, Path=IsChecked}"
ItemsSource="{Binding AvailableDrinks}" SelectedValue="{Binding SelectedDrink}"/>
When something is written into the textbox and then removed, the button isn't disabled.There is no problem with the combo box.
It is quite easy to do it
<StackPanel>
<StackPanel.Resources>
<BooleanToVisibilityConverter x:Key="VisibilityConverter" />
</StackPanel.Resources>
<GroupBox>
<GroupItem>
<StackPanel>
<RadioButton Content="Food" x:Name="rdbFood" IsChecked="{Binding IsFoodProperty}" ></RadioButton>
<RadioButton Content="Drinks" x:Name="rdbDrinks" IsChecked="{Binding IsDrinkProperty}"></RadioButton>
</StackPanel>
</GroupItem>
</GroupBox>
<TextBox Text="{Binding TextBox1}" Visibility="{Binding ElementName=rdbFood, Path=IsChecked, Converter={StaticResource VisibilityConverter}}"></TextBox>
<TextBox Text="{Binding TextBox2}" Visibility="{Binding ElementName=rdbFood, Path=IsChecked, Converter={StaticResource VisibilityConverter}}"></TextBox>
<ComboBox SelectedItem="{Binding SelectedDrink}" Visibility="{Binding ElementName=rdbDrinks, Path=IsChecked, Converter={StaticResource VisibilityConverter}}"></ComboBox>
<Button Content="Submit" Command="{Binding EnableProcessButton}"></Button>
</StackPanel>
EDIT
View Model
public string SelectedConMode { get; set; }
private bool? _isFoodProperty;
public bool? IsFoodProperty
{
get { return _isFoodProperty; }
set
{
_isFoodProperty = value;
if (value != null && (bool)value)
SelectedConMode = "Food";
}
}
private bool? _isDrinkProperty;
public bool? IsDrinkProperty
{
get { return _isDrinkProperty; }
set
{
_isDrinkProperty = value;
if (value != null && (bool)value)
SelectedConMode = "Drinks";
}
}
private string _selectedDrink;
public string SelectedDrink
{
get { return _selectedDrink; }
set
{
_selectedDrink = value;
}
}
private string _textBox1;
public string TextBox1
{
get { return _textBox1; }
set
{
_textBox1 = value;
}
}
private int _textBox2;
public int TextBox2
{
get { return _textBox2; }
set
{
_textBox2 = value;
}
}
public MainWindowViewModel()
{
IsFoodProperty = true;
IsDrinkProperty = false;
EnableProcessButton = new RelayCommand(() => ExecuteButton(null), () => CanExecuteButton(null));
}
public ICommand EnableProcessButton
{
get;
internal set;
}
private bool CanExecuteButton(object obj)
{
switch (SelectedConMode)
{
case "Drinks":
return (!string.IsNullOrEmpty(SelectedDrink));
case "Food":
return ((!string.IsNullOrEmpty(_textBox1)) && (_textBox2 != default(int)));
default:
return false;
}
}
private void ExecuteButton(object obj)
{
//DO the required things.
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Just in case you haven't hooked your CanExecuteChanged event to the CommandManager’s RequerySuggested:
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
And for more information about this, have a look here :
https://joshsmithonwpf.wordpress.com/2008/06/17/allowing-commandmanager-to-query-your-icommand-objects/
I declared two booleans in the viewmodel which is set in the TextChanged event handlers of the two textboxes. And the CanExecute function becomes
private bool CanExecuteButton(object obj)
{
switch (SelectedMode)
{
case "Drinks": return (!string.IsNullOrEmpty(SelectedDrink));
break;
case "Food": return (enableButtonFoodbox1 && enableButtonFoodbox2);//These two booleans are initialized to false and set to true in the TextChangedEvent Handlers of the two textboxes
break;
default: return false;
break;
}
}
Now, its working properly. Thanks.
I know I should use the MVVM pattern but I'm trying to get step by step closer to it. So here is my Listbox:
<ListBox x:Name="BoardList" ItemsSource="{Binding notes}" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<TextBox IsReadOnly="True" ScrollViewer.VerticalScrollBarVisibility="Visible" Text="{Binding text}" TextWrapping="Wrap" Foreground="DarkBlue"></TextBox>
<AppBarButton Visibility="{Binding visibility}" Icon="Globe" Click="OpenInBrowser" x:Name="Link"></AppBarButton>
<AppBarButton Icon="Copy" Click="Copy"></AppBarButton>
<AppBarButton Icon="Delete" Click="Delete"></AppBarButton>
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In the Mainpage.xaml.cs I declare the following:
ObservableCollection<BoardNote> notes = new ObservableCollection<BoardNote>();
So if I understood this right I don't need to care about the "INotifyCollectionChanged" stuff because I'm using an observablecollection?
So I got for example a textbox like this:
<Textbox x:Name="UserInputNote" Placeholdertext="Type in a text for your note"></Textbox>
And a button to Add the new note to the ObservableCollection and the click event is just like this:
notes.Add(new BoardNote(UserInputNote.Text));
So now the UI should update every time the user clicks the button to save a new note. But nothing happens. What did I do wrong?
If you need it here is the BoardNote class:
class BoardNote
{
public string text
{
get; set;
}
public BoardNote(string text)
{
this.text = text;
}
public Visibility visibility
{
get
{
if (text.StartsWith("http"))
return Visibility.Visible;
else
return Visibility.Collapsed;
}
}
}
You need to implement INotifyPropertyChanged. Here's one way of doing it.
Create this NotificationObject class.
public class NotificationObject : INotifyPropertyChanged
{
protected void RaisePropertyChanged<T>(Expression<Func<T>> action)
{
var propertyName = GetPropertyName(action);
RaisePropertyChanged(propertyName);
}
private static string GetPropertyName<T>(Expression<Func<T>> action)
{
var expression = (MemberExpression)action.Body;
var propertyName = expression.Member.Name;
return propertyName;
}
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
Then your BoardNote class will inherit it this way:
class BoardNote : NotificationObject
{
private string _text
public string Text
{
get {return _text;}
set
{
if(_text == value) return;
_text = value;
RaisePropertyChanged(() => Text);
}
}
public BoardNote(string text)
{
this.text = text;
}
public Visibility visibility
{
get
{
if (text.StartsWith("http"))
return Visibility.Visible;
else
return Visibility.Collapsed;
}
}
}
I have a ListBox created by ItemTemplate and Binding
<controls:PanoramaItem Header="{Binding AppResources.SettingsSubpage2, Source={StaticResource LocalizedStrings}}" HeaderTemplate="{StaticResource HeaderTemplate}">
<Grid>
<ListBox x:Name="DayOfWeekSelector" ItemTemplate="{StaticResource DayOfWeekTemplate}" ItemsSource="{Binding DayOfWeekElementList}" Foreground="{StaticResource AppForegroundColor}" LostFocus="DayOfWeekSelector_LostFocus" HorizontalAlignment="Left" Width="420" />
</Grid>
</controls:PanoramaItem>
Template code:
<phone:PhoneApplicationPage.Resources>
<!--- ... --->
<DataTemplate x:Key="DayOfWeekTemplate">
<Grid Height="65" Width="332">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<CheckBox IsChecked="{Binding IsActive, Mode=TwoWay}" Tag="{Binding}" d:LayoutOverrides="Width, Height" BorderBrush="{StaticResource AppBackgroundColor}" Background="{StaticResource ScheduleBackgroundAccentsColor}" Grid.Column="0" Unchecked="CheckBox_Unchecked" />
<StackPanel Grid.Column="1" Orientation="Horizontal">
<TextBlock Text="{Binding Name}" VerticalAlignment="Center" d:LayoutOverrides="Width"/>
<TextBlock Text="{Binding TaskCounter, Mode=OneWay, Converter={StaticResource DayOfWeekCounter}}" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="10,0,0,0"/>
</StackPanel>
</Grid>
</DataTemplate>
<!--- ... --->
And it's working fine. I've got all my items on the list. Checkboxes are binded to appropriate elements (clicking on it is changing proper value).
But by default ListBox can be also selected. Selection high-light TextBox binded to Name but don't change CheckBox (binded to IsActive). How can I tie item selection changing to checkbox state changing (in Silverlight)?
Edit:
public partial class SettingsPage : PhoneApplicationPage, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public List<DayOfWeekElement> DayOfWeekList
{
get
{
return CyberSyncPlanBase.Instance.DayOfWeekElementList;
}
set
{
CyberSyncPlanBase.Instance.DayOfWeekElementList = value;
NotifyPropertyChanged("DayOfWeekList");
}
}
public SettingsPage()
{
InitializeComponent();
DayOfWeekSelector.DataContext = CyberSyncPlanBase.Instance;
}
private void DayOfWeekSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
DayOfWeekElement dowe = (DayOfWeekElement) DayOfWeekSelector.SelectedItem;
if (dowe != null)
dowe.IsActive = (dowe.IsActive) ? false : true;
}
And in singleton INotifyPropertyChanged i've implemented in the same way:
private List<DayOfWeekElement> dayOfWeekElementList;
public List<DayOfWeekElement> DayOfWeekElementList
{
get { return dayOfWeekElementList; }
set
{
dayOfWeekElementList = value;
RecalcWeekTasks();
NotifyPropertyChanged("DayOfWeekElementList");
}
}
Bottom class:
public class DayOfWeekElement
{
public string Name { get { return this.DayOfWeek.ToStringValue(); } }
public bool IsNotEmpty { get { return (TaskCounter > 0); } }
public int TaskCounter { get; set; }
public bool IsActive { get; set; }
public DayOfWeek DayOfWeek { get; set; }
}
I think you could use the SelectedItem property of the ListBox control.
A possible implementation could be this:
Subscribe to the event SelectedIndexChanged of the ListBox.
Get the selected item.
For the selected item, change its IsActive property to true.
This works if the interface INotifyPropertyChanged is implemented in your data class.
E.g.:
public class DayOfWeekElement : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private bool isActive = false;
public bool IsActive {
get
{
return this.isActive;
}
set
{
if (value != this.isActive)
{
this.isActive= value;
NotifyPropertyChanged("IsActive");
}
}
}
}