I'm trying to use radiobuttons to enable a combobox, though there is an error within an if statement that is saying that I am not able to add .Checked or .IsChecked. As far as I am aware there is no namespace required that isn't already added to the classes by default.
I have tried copying online tutorials exactly yet the same error still occurs or even more errors occur.
<Grid>
<ComboBox x:Name="comboBox" HorizontalAlignment="Left" Margin="354,186,0,0" VerticalAlignment="Top" Width="120"/>
<RadioButton x:Name="rdoEnable" Content="Enable" HorizontalAlignment="Left" Margin="293,143,0,0" VerticalAlignment="Top"/>
<RadioButton x:Name="rdoDisable" Content="Disable" HorizontalAlignment="Left" Margin="471,143,0,0" VerticalAlignment="Top"/>
</Grid>
namespace RadioButtonTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
string[] array = new string[] { "One", "Two", "Three" };
foreach (string number in array)
{
comboBox.Items.Add(number);
}
rdoEnable.Checked = true;
}
private void RdoEnable_Checked(object sender, RoutedEventArgs e)
{
if (rdoDisable.Checked)
{
comboBox.Enabled = false;
}
else
{
comboBox.Enabled = true;
}
}
}
}
I've posted a couple of examples of things that I have tried to imgur if it is any help.
https://imgur.com/a/1sfqDfK
RadioButton.Checked is an event, you can not assign bool value to it, only attach event handler (method, which will be invoked when event is raised)
RadioButton.IsChecked is a property which gives current state. It has Nullable<bool> type, so condition should be in form
if (rdoDisable.IsChecked == true) {} else {}
or simple:
comboBox.IsEnabled = (rdoDisable.IsChecked == true);
Related
I have two textboxes inside an expander. I am validating the textbox for text value when a button is clicked. If the textbox is empty the textbox is given foucs.
What I want to achieve is for the expander to expand automatically when one of these texboxes gets focus.
I could not find any ways to do that on the internet. Is it possible to do so?
xaml :
<Grid>
<Expander Header="Textbox Expander">
<StackPanel Orientation="Vertical">
<TextBox Name="txtName" Height="30" Width="100" Background="WhiteSmoke" />
<TextBox Name="txtAge" Height="30" Width="100" Background="WhiteSmoke" />
</StackPanel>
</Expander>
<Button Name="btnDone" Content="Done" Width="50" Height="50" Click="btnDone_Click"/>
</Grid>
c# :
using System.Windows;
using System.Windows.Controls;
namespace TestExpanderFocus
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btnDone_Click(object sender, RoutedEventArgs e)
{
Validation validation = new Validation();
if(validation.validate(ref txtName) && validation.validate(ref txtAge))
{
//Do Something.
}
}
}
}
EDIT : Since this validation class is in another application I cannot edit this.
//Seperate class in another application which cannot be edited
public class Validation
{
public bool validate(ref TextBox txtobj)
{
if (string.IsNullOrWhiteSpace(txtobj.Text))
{
MessageBox.Show("Please Enter Data..!");
txtobj.Focus();
return false;
}
return true;
}
}
What you wanted to achieve is actually pretty simple.
First give the Expander a name
<Expander x:Name="MyExpander" ... />
Second, in your validation, before you focus on the textbox, simply expand the Expander
MyExpander.IsExpanded = true;
...
-- EDIT to satisy the new requirement --
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btnDone_Click(object sender, RoutedEventArgs e)
{
Validation validation = new Validation();
// since you know that the text will be focused when the validation fails
var result1 = validation.validate(ref txtName);
var result2 = validation.validate(ref txtAge);
MyExpander.IsExpanded = !result1 || !result2;
if(result1 && result2)
{
//Do Something.
}
}
}
But I must admit, this is not the nicest solution. There should be an easier way to just add Trigger to the Expander Style directly. (I will leave it to other people, since I don't have more time)
Description
I'm trying to build this simple UWP MVVM notetaking app.
The purpose of the app is adding text from a Textbox to a ListView, when a Add Button is clicked, or delete an item of the ListView by pressing a Delete Button, which is assigned to each item in the ListView.
Adding item to the ObservableCollection
Adding items to the ObservableCollection<Note> seems to work fine. The items are shown in the ListView without any problems.
Deleting items in the ObservableCollection
Deleting item doesn't work as it should.
My debug attempt
I tried to invoke the method responsible for deleting the items from both the constructor and the Delete Button.
When I invoke DoDeleteNote(Note itemToDelete) from the Delete Button, nothing happens, but if i invoke the same method from the constructor then the item is deleted.
I created a breakpoint in the DoDeleteNote(Note itemToDelete) method, and I can see in the debugger that it runs through the code, but nothing is deleted from the ObservableCollection<Note>.
However when i invoke the DoDeleteNote(Note itemToDelete) method from the constructor the item is deleted.
It is also strange, that the Note items I create and add to the ObservableCollection<Note> from the NoteViewModel constructor are the only items that are in the ObservableCollection<Note>. The items I add using the Add Button, are gone, but still shown in the ListView.
I’m thinking maybe something is wrong with either INotifyPropertyChanged or the bindings, but i’m not sure where to start looking, and what to look for, so I could use some help.
I know there seems to be a lot of code here, but I felt it was necessary not to omit anything to understand the data flow.
XAML
<Page
x:Class="ListView2.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ListView2"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModel="using:ListView2.ViewModel"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.DataContext>
<viewModel:NoteViewModel/>
</Grid.DataContext>
<ListView Header="Notes"
HorizontalAlignment="Left"
Height="341"
Width="228"
VerticalAlignment="Top"
Margin="163,208,0,0"
ItemsSource="{Binding Notes, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListView.ItemTemplate>
<DataTemplate x:Name="MyDataTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="TbxblListItem" Text="{Binding NoteText}"/>
<Button Command="{Binding DeleteNoteCommand}"
CommandParameter="{Binding ElementName=TbxblListItem}">
<Button.DataContext>
<viewModel:NoteViewModel/>
</Button.DataContext>
<Button.Content>
<SymbolIcon Symbol="Delete"
ToolTipService.ToolTip="Delete Note"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Button.Content>
</Button>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<TextBox x:Name="TbxNoteContent" HorizontalAlignment="Left"
Margin="571,147,0,0"
TextWrapping="Wrap"
VerticalAlignment="Top"
Width="376"/>
<Button Content="Add"
HorizontalAlignment="Left"
Margin="597,249,0,0"
VerticalAlignment="Top"
Command="{Binding AddNoteCommand}"
CommandParameter="{Binding Text, ElementName=TbxNoteContent}"/>
</Grid>
</page>
Note
namespace ListView2.Model
{
class Note
{
private string _noteText;
public Note(string noteText)
{
NoteText = noteText;
}
public string NoteText { get { return _noteText; } set { _noteText = value; } }
}
}
Notification
using System.ComponentModel;
namespace ListView2.Model
{
class Notification : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
NoteViewModel
using System.Collections.ObjectModel;
using Windows.UI.Xaml.Controls;
using ListView2.Model;
namespace ListView2.ViewModel
{
class NoteViewModel : Notification
{
#region Instance Fields
private ObservableCollection<Note> _notes;
private RelayCommand _addNoteCommand;
private RelayCommand _deleteNoteCommand;
//private string _noteText;
#endregion
#region Constructors
public NoteViewModel()
{
//adds sample data to Notes property (ObservableCollection<Note>)
Notes = new ObservableCollection<Note>() { new Note("Sample text 1"), new Note("Sample text 2") };
//Used for testing the deletion of items from ObservableCollection-------------------------------------
Notes.RemoveAt(1);
Notes.Add(new Note("Sample text 3"));
//foreach (var item in Notes)
//{
// if (item.NoteText == "Sample text 3")
// {
// DoDeleteNote(item);
// break;
// }
//}
//------------------------------------------------------
//Button command methods are added to delegates
AddNoteCommand = new RelayCommand(DoAddNote);
DeleteNoteCommand = new RelayCommand(DoDeleteNote);
}
#endregion
#region Properties
public ObservableCollection<Note> Notes { get { return _notes; } set { _notes = value; OnPropertyChanged("Notes"); } }
//public string NoteText { get { return _noteText; } set { _noteText = value; OnPropertyChanged("NoteText"); } }
public RelayCommand AddNoteCommand { get { return _addNoteCommand; } set { _addNoteCommand = value; } }
public RelayCommand DeleteNoteCommand { get { return _deleteNoteCommand; } set { _deleteNoteCommand = value; } }
#endregion
#region methods
private void DoAddNote(object obj)
{
var newItem = obj as string;
if (!string.IsNullOrEmpty(newItem))
{
AddNote(newItem);
}
}
//Work in progress
private void DoDeleteNote(object obj)
{
//Used when the XAML Delete Button invokes this method
TextBlock textBlockSender = obj as TextBlock;
//string myString = textBlockSender.Text;
Note itemToDelete = textBlockSender.DataContext as Note;
//Used when the constuctor invokes this method, for testing purposes------------
//Note itemToDelete = obj as Note;
//--------------------------------------------------------
foreach (Note note in this.Notes)
{
if (note.NoteText == itemToDelete.NoteText)
{
//int noteIndex = Notes.IndexOf(note);
//Notes.RemoveAt(noteIndex);
DeleteNote(note);
break;
}
}
//if (Notes.Contains(itemToDelete))
//{
// Notes.Remove(itemToDelete);
//}
}
public void AddNote(string noteText)
{
this.Notes.Add(new Note(noteText));
}
public void DeleteNote(Note itemToDelete)
{
this.Notes.Remove(itemToDelete);
}
#endregion
}
}
RelayCommand class which is the implementation for ICommand does not seem relevant for the question, so I haven't included it here, but if you are curious it can be seen on GitHub
As #Eugene Podskal points out one of the problems is with this code
<Button.DataContext>
<viewModel:NoteViewModel/>
</Button.DataContext>
Your Layout Grid instantiates a new NoteViewModel and the above code will do the same leaving you with 2 NoteViewModels active on the page.
Firstly give the ListView a name
<ListView x:Name="MyList" Header="Notes"
Next let's fix the Bindings on your DataTemplate of ListView MyList
<ListView.ItemTemplate>
<DataTemplate x:Name="MyDataTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="TbxblListItem" Text="{Binding NoteText}"/>
<Button Command="{Binding DataContext.DeleteNoteCommand, ElementName=MyList}"
CommandParameter="{Binding}">
<SymbolIcon Symbol="Delete"
ToolTipService.ToolTip="Delete Note"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Button>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
this line
Command="{Binding DataContext.DeleteNoteCommand, ElementName=MyList}"
means we are now Binding to the DataContext of MyList which is your NoteViewModel as defined under the main Grid
The CommandParamter is simplified to
CommandParameter="{Binding}"
as I explain below it is better practice to Bind to the object in your MyList which in this case is an object of Note
To make this work we need to adjust your NoteViewModel slightly
Change your private delete field and public property to
private RelayCommand<Note> _deleteNoteCommand;
public RelayCommand<Note> DeleteNoteCommand { get { return _deleteNoteCommand; } set { _deleteNoteCommand = value; } }
and in the constructor
DeleteNoteCommand = new RelayCommand<Note>(DoDeleteNote);
DoDeleteNote method is simplified to
private void DoDeleteNote(Note note)
{
this.Notes.Remove(note);
}
so we handily get rid of casting from a TextBlock. You can now get rid of the DeleteNote method as it's no longer needed.
Finally we need to add a new RelayCommand that takes a Generic Type to make our Command DeleteNoteCommand work correctly.
RelayCommand
public class RelayCommand<T> : ICommand
{
#region Fields
private readonly Action<T> _execute = null;
private readonly Predicate<T> _canExecute = null;
#endregion
#region Constructors
/// <summary>
/// Creates a new command that can always execute.
/// </summary>
/// <param name="execute">The execution logic.</param>
public RelayCommand(Action<T> execute)
: this(execute, null)
{
}
/// <summary>
/// Creates a new command with conditional execution.
/// </summary>
/// <param name="execute">The execution logic.</param>
/// <param name="canExecute">The execution status logic.</param>
public RelayCommand(Action<T> execute, Predicate<T> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion
#region ICommand Members
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute((T)parameter);
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
var handler = CanExecuteChanged;
if (handler != null)
CanExecuteChanged(this, new EventArgs());
}
public void Execute(object parameter)
{
_execute((T)parameter);
}
#endregion
}
Apologies this answer is long but I wanted to point out every step. I also suggest using a mvvm framework when working with xaml as it will make your life alot easier. I recommend mvvmlight but there are plenty of others. Hope that helps
I have a simple WPF app with 3 textboxes, 2 of the text boxes input numbers and the third textbox shows the sum of inputs when another button is clicked.
I come from WinForms and MFC background and for me, the intuitive thing to do is to right click the textBoxes, open their properties and specify local variables to read the data from the boxes. For example, MFC has the DDX mechanism for this.
However, in WPF, the only way to specify a binding seems to add XAML code directly to App.XAML, as shown here on MSDN. Is there a way to create a binding without coding it manually into XAML? XAML coding seems a little daunting to me, since I am new to it.
My WPF form is as follows :
<Window x:Class="SimpleAdd.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="174,43,0,0" TextWrapping="Wrap" Text="{Binding dataModel.Value1}" VerticalAlignment="Top" Width="120"/>
<TextBox HorizontalAlignment="Left" Height="23" Margin="174,84,0,0" TextWrapping="Wrap" Text="{Binding dataModel.Value2}" VerticalAlignment="Top" Width="120"/>
<TextBox HorizontalAlignment="Left" Height="23" Margin="174,127,0,0" TextWrapping="Wrap" Text="{Binding dataModel.Value3}" VerticalAlignment="Top" Width="120"/>
<Button Content="Add" HorizontalAlignment="Left" Margin="393,84,0,0" VerticalAlignment="Top" Width="75" Click="OnAdd"/>
</Grid>
</Window>
My C# file is as follows :
namespace SimpleAdd
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void OnAdd(object sender, RoutedEventArgs e)
{
dataModel m1 = new dataModel();
m1.Value3 = m1.Value1 + m1.Value2; // BUG : All Properties are 0 even after updating the boxes.
}
}
public class dataModel
{
private int val1, val2, val3;
public int Value1
{
get {return val1;}
set { val1 = value; }
}
public int Value2
{
get { return val2; }
set { val2 = value; }
}
public int Value3
{
get { return val3; }
set { val3 = value; }
}
}
}
EDIT : Adding implementation for INotifyPropertyChanged
namespace SimpleAdd
{
public abstract class ObservableObject : INotifyPropertyChanged
{
#region Debugging Aides
/// <summary>
/// Warns the developer if this object does not have
/// a public property with the specified name. This
/// method does not exist in a Release build.
/// </summary>
[Conditional("DEBUG")]
[DebuggerStepThrough]
public virtual void VerifyPropertyName(string propertyName)
{
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = "Invalid property name: " + propertyName;
if (this.ThrowOnInvalidPropertyName)
throw new Exception(msg);
else
Debug.Fail(msg);
}
}
/// <summary>
/// Returns whether an exception is thrown, or if a Debug.Fail() is used
/// when an invalid property name is passed to the VerifyPropertyName method.
/// The default value is false, but subclasses used by unit tests might
/// override this property's getter to return true.
/// </summary>
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
#endregion // Debugging Aides
#region INotifyPropertyChanged Members
/// <summary>
/// Raises the PropertyChange event for the property specified
/// </summary>
/// <param name="propertyName">Property name to update. Is case-sensitive.</param>
public virtual void RaisePropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
OnPropertyChanged(propertyName);
}
/// <summary>
/// Raised when a property on this object has a new value.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises this object's PropertyChanged event.
/// </summary>
/// <param name="propertyName">The property that has a new value.</param>
protected virtual void OnPropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
#endregion // INotifyPropertyChanged Members
}
}
Your TextBox are not being updated because you haven't set the data source (DataContext typically) behind the bindings.
When you write
<TextBox Text="{Binding dataModel.Value1}" />
What you are really saying "pull the value for this field from TextBox.DataContext.dataModel.Value1". If TextBox.DataContext is null, then nothing will be displayed.
The DataContext is inherited automatically, so the following code would work :
public partial class MainWindow : Window
{
public dataModel _data { get; set; }
public MainWindow()
{
InitializeComponent();
_data = new dataModel();
this.DataContext = _data;
}
private void OnAdd(object sender, RoutedEventArgs e)
{
_data.Value3 = _data.Value1 + _data.Value2;
}
}
assuming you also change your TextBox bindings to remove the dataModel. from them
<TextBox Text="{Binding Value1}" />
This sets the DataContext of the entire form to the _data object, and in your OnAdd method we can update the _data object properties in order to have the UI update.
I like to blog a bit about beginner WPF stuff, and you may be interested in checking out a couple of the posts there which explain these concepts :
Understanding the change in mindset when switching from WinForms to WPF
What is this "DataContext" you speak of?
Technically, that's not in App.xaml (which is a special file in WPF).
That said, yes you can do it. You can set up the binding in code like so:
textBox1.Text = new Binding("SomeProperty");
...
Okay, that gets really annoying so we just do it in XAML:
<TextBox Text="{Binding SomeProperty}"/>
Both pieces of code do the same thing, but when you get into more advanced bindings, the XAML syntax is a lot easier to use. Plus, its more obvious where you text is coming from rather than having to open two files.
The FrameworkElement class and the FrameworkContentElement class both expose a SetBinding method. If you are binding an element that inherits either of these classes, you can call the SetBinding method directly.
The following example creates a class named, MyData, which contains a property named MyDataProperty.
public class MyData : INotifyPropertyChanged
{
private string myDataProperty;
public MyData() { }
public MyData(DateTime dateTime)
{
myDataProperty = "Last bound time was " + dateTime.ToLongTimeString();
}
public String MyDataProperty
{
get { return myDataProperty; }
set
{
myDataProperty = value;
OnPropertyChanged("MyDataProperty");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
}
The following example shows how to create a binding object to set the source of the binding. The example uses SetBinding to bind the Text property of myText, which is a TextBlock control, to MyDataProperty.
MyData myDataObject = new MyData(DateTime.Now);
Binding myBinding = new Binding("MyDataProperty");
myBinding.Source = myDataObject;
myText.SetBinding(TextBlock.TextProperty, myBinding);
Ive been playing with wpf and 2 way data binding to better understand it and ive noticed that when a textbox has 2 way data binding to a property the property is called twice. I have verified this by writing a value to the output window when the property is called. My code is below:-
My xaml
<Page
x:Class="_2waybindTest.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:_2waybindTest"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<TextBox HorizontalAlignment="Left" Margin="55,93,0,0" TextWrapping="Wrap" Text="{Binding TestProperty, Mode=TwoWay}" VerticalAlignment="Top" Width="540"/>
<Button Content="Button" HorizontalAlignment="Left" Margin="55,31,0,0" VerticalAlignment="Top" Click="Button_Click_1"/>
<TextBox HorizontalAlignment="Left" Margin="55,154,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="540"/>
</Grid>
</Page>
my simple viewmodel class to test
public class viewmodel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _TestProperty;
public void SetTestProperty()
{
this.TestProperty = "Set Test Property";
}
public string TestProperty{
get
{
return this._TestProperty;
}
set
{
this._TestProperty = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("TestProperty"));
}
Debug.WriteLine("this._TestProperty = " + this._TestProperty);
}
}
}
my xaml code behind
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
DataContext = new viewmodel();
}
/// <summary>
/// Invoked when this page is about to be displayed in a Frame.
/// </summary>
/// <param name="e">Event data that describes how this page was reached. The Parameter
/// property is typically used to configure the page.</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
var vm = (viewmodel)DataContext;
vm.SetTestProperty();
}
}
Why is it called twice. Is this expected behaviour?
Generally, you should check if value actually changed, before firing a propertyChanged event, otherwise you may get into infinte cycle of binding updates. In your case, textbox is probably checking for change, preventing such cycle.
public string TestProperty{
set
{
if(this._TestProperty == value)
{
return;
}
this._TestProperty = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("TestProperty"));
}
}
}
Im trying to create a list of classes for a degree plan at my university where when classes that have been taken are checked another class gets highlighted to let the user know that class has all of the prerequisites met to be taken. so if i check calculus 1, physics 1 will get highlighted.
Im new to C# and i dont have a heavy knowledge of what the language and .NET framework can do so im asking for a good simple straight answer, if you could explain exactly what is going on in the code that would be fantastic. Thanks
heres what i have so far. just a basic proof of concept WPF
<Window x:Class="degree_plan.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Degree Planner" Height="350" Width="525" Name="Degree">
<Grid>
<CheckBox Content="Math 1412" Height="16" HorizontalAlignment="Left" Margin="34,40,0,0" Name="checkBox1" VerticalAlignment="Top" />
<CheckBox Content="Physics 1911" Height="16" HorizontalAlignment="Left" Margin="34,62,0,0" Name="checkBox2" VerticalAlignment="Top" />
</Grid>
</Window>
and heres the c# code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace degree_plan
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// if (checkBox1.Checked)
// Console.WriteLine("physics 1");
}
}
}
You can register an event handler for the checkboxes:
AddHandler(CheckBox.CheckedEvent, new RoutedEventHandler(CheckBox_Click));
then, create the event handler:
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
CheckBox checkbox = e.Source as CheckBox
//...
}
The checkbox variable in the event handler is the checkbox which was clicked to raise the event. You can check which checkbox it was, and then enable all the options which depend on it.
I know you asked for simple but at some point you can come back to this as it's a very structured and expandable way to hold and use data in WPF
I would consider quantifying the Classes in their own structure, each with a list of the prerequisite classes that must be completed before hand, I would like to suggest using the following to achieve what you're after (sorry, bit long!)
what you'll get is a list of classes represented by checkboxes, you can only check a class once all of its prerequisite classes are complete, they have names and descriptions and can be customised on the UI in anyway you want.
Create a new WPF application and add the following Class.
Class.cs
public class Class : Control, INotifyPropertyChanged
{
// Property that's raised to let other clases know when a property has changed.
public event PropertyChangedEventHandler PropertyChanged;
// Flags to show what's going on with this class.
bool isClassComplete;
bool isPreComplete;
// Some other info about the class.
public string ClassName { get; set; }
public string Description { get; set; }
// A list of prerequisite classes to this one.
List<Class> prerequisites;
// public property access to the complete class, you can only set it to true
// if the prerequisite classes are all complete.
public bool IsClassComplete
{
get { return isClassComplete; }
set
{
if (isPreComplete)
isClassComplete = value;
else
if (value)
throw new Exception("Class can't be complete, pre isn't complete");
else
isClassComplete = value;
PropertyChangedEventHandler temp = PropertyChanged;
if (temp != null)
temp(this, new PropertyChangedEventArgs("IsClassComplete"));
}
}
// public readonly property access to the complete flag.
public bool IsPreComplete { get { return isPreComplete; } }
public Class()
{
prerequisites = new List<Class>();
isPreComplete = true;
}
// adds a class to the prerequisites list.
public void AddPre(Class preClass)
{
prerequisites.Add(preClass);
preClass.PropertyChanged += new PropertyChangedEventHandler(preClass_PropertyChanged);
ValidatePre();
}
// removes a class from the prerequisites lists.
public void RemovePre(Class preClass)
{
prerequisites.Remove(preClass);
preClass.PropertyChanged -= new PropertyChangedEventHandler(preClass_PropertyChanged);
ValidatePre();
}
// each time a property changes on one of the prerequisite classes this is run.
void preClass_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "IsClassComplete":
// check to see if all prerequisite classes are complete/
ValidatePre();
break;
}
}
void ValidatePre()
{
if (prerequisites.Count > 0)
{
bool prerequisitesComplete = true;
for (int i = 0; i < prerequisites.Count; i++)
prerequisitesComplete &= prerequisites[i].isClassComplete;
isPreComplete = prerequisitesComplete;
if (!isPreComplete)
IsClassComplete = false;
}
else
isPreComplete = true;
PropertyChangedEventHandler temp = PropertyChanged;
if (temp != null)
temp(this, new PropertyChangedEventArgs("IsPreComplete"));
}
}
Now in the code behind for MainWindow.cs you can create a collection of classes, I've done this in the constructor and provided an observable collection of classes so when new classes are added, you don't have to do anything to get them to display on the UI
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public ObservableCollection<Class> Classes
{
get { return (ObservableCollection<Class>)GetValue(ClassesProperty); }
set { SetValue(ClassesProperty, value); }
}
public static readonly DependencyProperty ClassesProperty = DependencyProperty.Register("Classes", typeof(ObservableCollection<Class>), typeof(MainWindow), new UIPropertyMetadata(null));
public MainWindow()
{
InitializeComponent();
Class math = new Class()
{
ClassName = "Math 1412",
Description = ""
};
Class physics = new Class()
{
ClassName = "Physics 1911",
Description = "Everything everywhere anywhen",
};
physics.AddPre(math);
Classes = new ObservableCollection<Class>();
Classes.Add(math);
Classes.Add(physics);
}
}
The final step is to tell WPF what a class is supposed to look like on the user interface, this is done in resources, to simplify the example, I've put it in the MainWindow.xaml file.
<Window x:Class="WpfApplication8.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication8"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<!-- This tells WPF what a class looks like -->
<Style TargetType="{x:Type local:Class}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:Class}">
<StackPanel Orientation="Horizontal" DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
<!-- Checkbox and a text label. -->
<CheckBox IsEnabled="{Binding IsPreComplete}" IsChecked="{Binding IsClassComplete}" />
<TextBlock Margin="5,0,0,0" Text="{Binding ClassName}" ToolTip="{Binding Description}"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<!-- This draws the list of classes from the property in MainWindow.cs-->
<ItemsControl ItemsSource="{Binding Classes}"/>
</Grid>
</Window>
For your ease,
try to use the 'CheckedChanged' event.. Just double click the CheckBox on the designer. Handler will be added automatically(something like,
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
}
.Then add your code there. But, this is time consuming(since, you have to add handler for each CheckBox). But, 'll be easy for you to understand at this stage.