I am writting a login window using WPF and C#,but got stuck when I attempted to retrive username from the TextBox.The class property which I bind to the TextBox always get a null value,and I cannot figure out why.
MainWindow.xaml
<Window x:Class="Databinding.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>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="256*"/>
<ColumnDefinition Width="261*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="Username" HorizontalAlignment="Stretch" Margin="0,0,0,0" VerticalAlignment="Stretch" HorizontalContentAlignment="Right" VerticalContentAlignment="Center" FontSize="20"/>
<Label Grid.Row="1" Grid.Column="0" Content="Password" HorizontalAlignment="Stretch" Margin="0,0,0,0" VerticalAlignment="Stretch" HorizontalContentAlignment="Right" VerticalContentAlignment="Center" FontSize="20"/>
<Button Content="Confirm" Click="Confirm_Click" IsDefault="True" Grid.Row="2" Grid.Column="0" HorizontalAlignment="Stretch" Margin="0,0,0,0" VerticalAlignment="Stretch" FontSize="20"/>
<Button Content="Cancel" Click="Cancel_Click" IsCancel="True" Grid.Row="2" Grid.Column="1" HorizontalAlignment="Stretch" Margin="0,0,0,0" VerticalAlignment="Stretch" FontSize="20"/>
<TextBox x:Name="TextUsername" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Stretch" Margin="0,0,0,0" TextWrapping="Wrap" VerticalAlignment="Stretch" ToolTip="Enter your username"/>
<PasswordBox x:Name="Password" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Stretch" Margin="0,0,0,0" VerticalAlignment="Stretch" ToolTip="Enter your password"/>
</Grid>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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;
using System.ComponentModel;
namespace Databinding
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Confirm_Click(object sender, RoutedEventArgs e)
{
Login LoginInfo = new Login();
Binding bindingLogin = new Binding();
bindingLogin.Source = LoginInfo;
bindingLogin.Path = new PropertyPath("Username");
bindingLogin.Mode = BindingMode.TwoWay;
bindingLogin.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
BindingOperations.SetBinding(this.TextUsername, TextBox.TextProperty, bindingLogin);
if(LoginInfo.Username=="admin" && this.Password.Password=="admin")
{
MessageBox.Show("Welcome!","Login Status");
}
else
{
MessageBox.Show("Something is wrong!","Login Status");
}
}
private void Cancel_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
}
public class Login:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string username;
public string Username
{
get
{
return username;
}
set
{
username = value;
if(this.PropertyChanged!=null)
{
this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Username"));
}
}
}
}
}
I know databinding is not a wise option in this case,and I can get things done more efficently by just using
string Username = this.TextUsername.Text
Anyway,this is a some demo,and I have to use databinding in my project.What's wrong with my code?
At the point you attach your binding, Login.Username is null, and since the binding is two way WPF will update your textbox to null to match.
Bindings are supposed to be active all the time, and declared in the XAML, rather than bound at the point you want data. You are defeating the object of using bindings. Like you say, it would be far easier to just grab the text directly if you are going to do it explicitly.
The problem is when user entering/changing Text in the TextUsername, your binding is not there yet, (your binding will only be there after the Confirm_Click, and will be reset with each Confirm_Click) so you need to move it to the constructor.
namespace Databinding
{
public partial class MainWindow : Window
{
Login LoginInfo;
public MainWindow()
{
InitializeComponent();
LoginInfo = new Login();
Binding bindingLogin = new Binding();
bindingLogin.Source = LoginInfo;
bindingLogin.Path = new PropertyPath("Username");
bindingLogin.Mode = BindingMode.TwoWay;
bindingLogin.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
BindingOperations.SetBinding(this.TextUsername, TextBox.TextProperty, bindingLogin);
}
//... rest
}
}
And you could easily apply the binding in XAML, and avoid all of these code behind if you can set the DataContext of your window correctly e.g.:
<TextBox x:Name="TextUsername" Text="{Binding Username}" ..../>
The below code will works, not sure if it's a good practice thou:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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;
using System.ComponentModel;
namespace Databinding
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
Login LoginInfo;
Binding bindingLogin;
public MainWindow()
{
InitializeComponent();
LoginInfo = new Login();
bindingLogin = new Binding();
bindingLogin.Source = LoginInfo;
bindingLogin.Path = new PropertyPath("Username");
bindingLogin.Mode = BindingMode.TwoWay;
bindingLogin.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
}
private void Confirm_Click(object sender, RoutedEventArgs e)
{
BindingOperations.SetBinding(this.TextUsername, TextBox.TextProperty, bindingLogin);
if(LoginInfo.Username=="admin" && this.Password.Password=="admin")
{
MessageBox.Show("Welcome!","Login Status");
}
else
{
MessageBox.Show("Something is wrong!","Login Status");
}
}
private void Cancel_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
}
public class Login:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string username;
public string Username
{
get
{
return username;
}
set
{
username = value;
if(this.PropertyChanged!=null)
{
this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Username"));
}
}
}
}
}
Related
I am making a To-do List application for one of my classes. Now I have tried in a console app to update objects (of TaskModels a type I defined) inside an observable collection and it has seemed to work. But now I am trying to update the entries inside the observable collection. I have wrote some code that I thought was going to change the value but it did not change the value in the ListBox I will include the code below for TaskModel.
Originally I was wanting to have the edit button open a new window that allowed the user to input what the wanted the task to be and then they press a button maybe called change task and it sends it back to the original window. But for simpleness at the moment I would like to change with the textbox in the user control in the main window.
I am also pretty new to making apps in WPF so this is all pretty new to me and I am trying to learn it all.
Below is my TaskModel and the only thing it does is get and set the TaskName.
namespace ToDoList.Model
{
public class TaskModel
{
private string taskName;
public string TaskName {
get { return taskName; }
set { taskName = value; }
}
}
}
I wrote the following code hoping that it would allow me to change the value of the TaskName but it does not seem to be working. Is there anyhting else that I should add to the code for it change the TaskName properly? Any tips or anything that could help me with this problem.
Below is the XAML code for my main window. It is a really simple UI, it features a ListBox which uses an ObservableCollection as its itemsource and when the listbox has a new item put into it has a checkbox to the left of it.
Below I will include the main window XAML and the C# code.
<Window x:Class="ToDoList.DemoMainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ToDoList"
mc:Ignorable="d"
Title="The To-Do List" Height="500" Width="500" FontSize="22"
Background="White">
<Grid Margin="10" Background="BlueViolet">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2">
<TextBlock>Tasks to do:</TextBlock>
<!-- <TextBlock>Blah blah</TextBlock> -->
<!-- <local:UCLabelTextBxInput x:Name="TxtUCSaveToFileLocation" Title="Save to File Location" MaxLength="50"></local:UCLabelTextBxInput> -->
<ListBox x:Name="LstBoxTasks" MinHeight="200" MaxHeight="200" SelectionMode="Multiple">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Mode=OneWay}" Content="{Binding TaskName, Mode=TwoWay}" FontSize="14"></CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<local:UCLabelTextBxInput x:Name="TxtUCEnteredTask" Title="Enter Task Here:" MaxLength="50"></local:UCLabelTextBxInput>
<Button x:Name="BtnAddTask" Click="BtnAddTask_Click" Background="Chocolate">Add Task to List</Button>
</StackPanel>
<Button Grid.Column="0" Grid.Row="1" Margin="0,10,20,0" x:Name="btnDeleteTask" Click="BtnDeleteTask_Click" Background="Chocolate">Delete Task</Button>
<Button Grid.Column="1" Grid.Row="1" Margin="20,10,0,0" Background="Chocolate" Click="BtnEditTask_Click">Edit Task</Button>
<Button Grid.Column="0" Grid.Row="2" Margin="0,10,0,10" Grid.ColumnSpan="2" Background="Chocolate" Click="BtnHelp_Click">Help</Button>
</Grid>
</Window>
Then finally here is the C# code:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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.Shapes;
using ToDoList.Model;
namespace ToDoList
{
/// <summary>
/// Interaction logic for DemoMainWindow.xaml
/// </summary>
public partial class DemoMainWindow : Window
{
SaveDataModel saveDataModel = new SaveDataModel();
ObservableCollection<TaskModel> tasksModels = new ObservableCollection<TaskModel>();
public DemoMainWindow()
{
InitializeComponent();
TxtUCEnteredTask.txtLimitedInput.Text = "Do the dishes";
LstBoxTasks.ItemsSource = tasksModels;
}
private void BtnAddTask_Click(object sender, RoutedEventArgs e)
{
tasksModels.Add(new TaskModel() { TaskName = TxtUCEnteredTask.txtLimitedInput.Text });
}
private void BtnDeleteTask_Click(object sender, RoutedEventArgs e)
{
if(LstBoxTasks.SelectedItem != null)
{
tasksModels.Remove(LstBoxTasks.SelectedItem as TaskModel);
}
}
private void BtnHelp_Click(object sender, RoutedEventArgs e)
{
HelpWindow helpWindow = new HelpWindow();
helpWindow.Show();
}
private void BtnEditTask_Click(object sender, RoutedEventArgs e)
{
if (LstBoxTasks.SelectedItem != null)
{
tasksModels[LstBoxTasks.SelectedIndex].TaskName = TxtUCEnteredTask.txtLimitedInput.Text;
}
}
}
}
Your property TaskName is not calling the OnPropertyChanged() method that notifies your view that the name changed.
You can do something like that:
private string taskName;
public string TaskName {
get { return taskName; }
set {
if( value != taskName) {
taskName = value;
OnPropertyChanged("TaskName");
}
}
}
protected void OnPropertyChanged(string name) {
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
Remarks:
The best way to do WPF is to do MVVM.
You should check out this link : https://www.codeproject.com/Tips/806587/Basic-MVVM-Listbox-Binding-in-WPF
Hi I this two different modes to binding visibility from a parent's property .
for standar control is possible tom use this:
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
</Window.Resources>
<Button x:Name="buttonTest" Width="75" Visibility="{Binding IsModify, Converter={StaticResource BoolToVis}}"/>
but to my usercontrol I only this way to binding the visibility , otherwise doesn't works:
<local:UserControl4 Height="100" Width="100" Visibility="{Binding ElementName=Window1,Path=IsModify,Converter={StaticResource BoolToVis}}"/>
My question is why I need to pass the element name plus ParentProperty at my control visibility , but for button no?
this is the codeBehind
public static readonly DependencyProperty IsModifyProperty =
DependencyProperty.Register("IsModify", typeof(Boolean), typeof(MainWindow), new PropertyMetadata(false));
[DefaultValue(false)]
public Boolean IsModify
{
get { return (Boolean)GetValue(MainWindow.IsModifyProperty); }
set
{
SetValue(MainWindow.IsModifyProperty, value);
this.OnPropertyChanged("IsModify");
}
}
and this is the constructor
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
here the source code to replicate the error.
you needs one project with one window and one usercontrol.
source code for window
---- XAML ----
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:System="clr-namespace:System;assembly=mscorlib" xmlns:local="clr-namespace:XTesting" x:Name="windowTest" x:Class="XTesting.WindowTest"
Title="WindowTest" Height="300" Width="583.908">
<Window.Resources>
<ResourceDictionary>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<local:UserControl4
HorizontalAlignment="Left"
Height="65"
Margin="300,30,0,0"
VerticalAlignment="Top"
Width="230"
Visibility="{Binding IsModify, Converter={StaticResource BoolToVis}}"
Background="#FFF86161" Description="Custom Control"/>
<Button x:Name="buttonTest"
Content="Modify"
HorizontalAlignment="Left"
Margin="45,30,0,0"
VerticalAlignment="Top"
Width="225"
Visibility="{Binding IsModify, Converter={StaticResource BoolToVis}}"
Height="65"/>
<Button x:Name="button"
Content="Skisem (Click me)"
HorizontalAlignment="Left"
Height="75"
Margin="225,160,0,0"
VerticalAlignment="Top"
Width="135"
Click="button_Click_1"
Cursor="Hand"/>
</Grid>
this is the codebehind of window
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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.Shapes;
namespace XTesting
{
/// <summary>
/// Interaction logic for WindowTest.xaml
/// </summary>
public partial class WindowTest : Window
{
public static readonly DependencyProperty IsModifyProperty =
DependencyProperty.Register("IsModify", typeof(Boolean), typeof(WindowTest), new PropertyMetadata(false));
[DefaultValue(false)]
public Boolean IsModify
{
get { return (Boolean)GetValue(WindowTest.IsModifyProperty); }
set
{
SetValue(WindowTest.IsModifyProperty, value);
this.OnPropertyChanged("IsModify");
}
}
public WindowTest()
{
InitializeComponent();
this.DataContext = this;
}
private void button_Click_1(object sender, RoutedEventArgs e)
{
IsModify = !IsModify;
}
#region - INotifyPropertyChanged implementation -
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion - INotifyPropertyChanged implementation -
}
}
this is the source code for usercontrol
XAML:
<UserControl x:Class="XTesting.UserControl4"
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"
mc:Ignorable="d"
d:DesignHeight="37" d:DesignWidth="310" MinWidth="7"
MouseLeftButtonUp="Selector1_MouseLeftButtonUp" FontSize="20">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid x:Name="GridRoot">
<Grid.RowDefinitions>
<RowDefinition Height="37*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="51"/>
<ColumnDefinition Width="259*"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="TextBoxDescription"
Margin="0"
TextWrapping="Wrap"
Grid.Column="1"
Text="{Binding Description}"
Background="{x:Null}"
BorderBrush="{x:Null}"
Foreground="{Binding Foreground,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
FontSize="{Binding FontSize,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
VerticalContentAlignment="Center"
IsReadOnly="True"
SelectionBrush="{x:Null}"
BorderThickness="0"
Focusable="False"
IsTabStop="False"
IsUndoEnabled="False"
AllowDrop="False"
Padding="1,0,0,0"
MaxLines="1"
/>
</Grid>
and this is the code behind:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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 XTesting
{
/// <summary>
/// Interaction logic for UserControl4.xaml
/// </summary>
public partial class UserControl4 : UserControl, INotifyPropertyChanged
{
#region - Delegate -
public delegate void ClickHandler(Object sender, RoutedEventArgs e);
#endregion - Delegate -
#region - Events -
public event ClickHandler Click;
#endregion - Events -
#region - Dependency Properties mandatory for Binding -
public static readonly DependencyProperty DescriptionProperty =
DependencyProperty.Register("Description", typeof(String), typeof(UserControl4), new PropertyMetadata(String.Empty));
#endregion - Dependency Properties for Binding -
#region - Properties -
/// <summary>
/// Gets or sets the description
/// </summary>
[Category("Common"), Description("gets or sets The description")]
public String Description
{
get { return (String)GetValue(UserControl4.DescriptionProperty); }
set
{
SetValue(UserControl4.DescriptionProperty, value);
this.OnPropertyChanged("Description");
}
}
#endregion - Properties -
public UserControl4()
{
InitializeComponent();
this.DataContext = this;
}
private void Selector1_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (Click != null) Click(sender, e);
}
#region - INotifyPropertyChanged implementation -
// Basically, the UI thread subscribes to this event and update the binding if the received Property Name correspond to the Binding Path element
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion - INotifyPropertyChanged implementation -
}
}
I think the problem is in the constructor This.datacontext= this;
but this is mandatory if i want binding the property description.
Thanks in advance
You should probably add the user control DataContext on the GridRoot element instead of the UserControl4 class itself. The user control usage should be controlled mainly by the instantiating code and only its content should be controlled by the user control itself.
Also, I prefer to assign the data context on the Loaded event instead of the constructor, but this might just be my personal preference.
<UserControl x:Class="XTesting.UserControl4" Loaded="UserControl4_Loaded" ...
// ...
private void UserControl4_Loaded(object sender, RoutedEventArgs e)
{
GridRoot.DataContext = this;
}
I am trying to create a custom command called WebBrowser which will open my default webbrowser and go to google.com when I click on it. So far I only made the exit command, and I a really stuck how to implement this command. Any help would be amazing.
The XAML is:
<Window x:Class="WpfTutorialSamples.Commands.CustomCommandSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:self="clr-namespace:WpfTutorialSamples.Commands"
Title="CustomCommandSample" Height="150" Width="200">
<Window.CommandBindings>
<CommandBinding Command="self:CustomCommands.Exit" CanExecute="ExitCommand_CanExecute" Executed="ExitCommand_Executed" />
</Window.CommandBindings>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Menu>
<MenuItem Header="My Command">
<MenuItem Command="self:CustomCommands.Exit" />
</MenuItem>
</Menu>
<StackPanel Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Window>
The code-behind is:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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 WpfTutorialSamples.Commands
{
public partial class CustomCommandSample : Window
{
public CustomCommandSample()
{
InitializeComponent();
}
private void ExitCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
private void ExitCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
Application.Current.Shutdown();
}
}
public static class CustomCommands
{
public static readonly RoutedUICommand Exit = new RoutedUICommand
(
"Exit",
"Exit",
typeof(CustomCommands),
new InputGestureCollection()
{
new KeyGesture(Key.F4, ModifierKeys.Alt)
}
);
}
}
I am trying to make it look like this:
http://i.stack.imgur.com/k8ezB.png
You need a command which starts a new process and gives it as an argument a URL. I am using DelegateCommand here but you can use any custom ICommand you already have:
public DelegateCommand StartChromeCommand = new DelegateCommand(OnStartChrome);
private void OnStartChrome()
{
var process = new Process(new ProcessStartInfo {Arguments = #"http://www.google.com"});
process.Start();
}
I'm populating a listBox, Each list item has a button.
I've got it populating text in to the list, but I'd like each button to have the correct event handler and the house number to be passed to it.
Here's the XAML code:
<Window x:Class="WpfApplication1.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" Loaded="Window_Loaded" Background="#FFCBCBCB">
<Grid>
<Label Content="Welcome to the house manager" Height="28" HorizontalAlignment="Left" Margin="20,12,0,0" Name="label1" VerticalAlignment="Top" />
<ListBox Height="253" HorizontalAlignment="Left" Margin="10,46,0,0" VerticalAlignment="Top" Width="481" x:Name="houseList">
<ListBox.ItemTemplate>
<DataTemplate DataType="house">
<Button Click="choose_house(HOUSE NUMBER HERE)" Background="#FFBEBEBE" BorderThickness="0" Focusable="True" Width="459" BorderBrush="White" Padding="0">
<StackPanel Orientation="Vertical" Width="400" VerticalAlignment="Stretch">
<TextBlock Margin="0,5,0,0" Text="{Binding street}" HorizontalAlignment="Left" />
<TextBlock Margin="0,5,0,0" Text="{Binding postCode}" HorizontalAlignment="Left" />
</StackPanel>
</Button>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
Here's the code which populates the list at the moment:
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 WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
List<house> houseAddresses = new List<house>();
// Create a new house
for (var i = 1; i <= 10; i++)
{
house newHouse = new house();
newHouse.street = i + " Scale Hall Lane";
newHouse.postCode = "PR4 3TL";
newHouse.houseNumber = i;
houseAddresses.Add(newHouse);
}
// Go through each house and add them to the list
foreach (house houses in houseAddresses)
{
houseList.Items.Add(houses);
}
}
private void choose_house(object sender, RoutedEventArgs e)
{
MessageBox.Show("Clicked");
}
}
}
Here's my house class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace WpfApplication1
{
class house
{
public string street { get; set; }
public string postCode { get; set; }
public int houseNumber { get; set; }
}
}
I've put Click="choose_house(HOUSE NUMBER HERE)" in the code on the button where the code would have the correct event handler.
Either cast your sender as a Button and get it's DataContext, or use a Command and bind the CommandParameter to the HouseNumber
private void choose_house(object sender, RoutedEventArgs e)
{
var ctrl = sender as Button;
var h = ctrl.DataContext as house; // Please capitalize your classes! :)
MessageBox.Show(h.houseNumber);
}
or
<Button Command="{Binding Path=DataContext.ChooseHouseCommand,
RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}"
CommandParameter="{Binding houseNumber}" ... />
What you can do on the click handler is:
private void choose_house(object sender, RoutedEventArgs e)
{
var button = sender as Button;
if (button != null)
{
var yourObject = button.DataContext as house;
if (yourObject != null)
{
//do stuff with your button
}
}
}
I've written a very short app in which I'm trying to achieve the following : have the CheckBox change its state from code. I've wired up the INotifyPropertyChanged interface and was expecting to see some results but apparently the app does nothing. Is there something wrong with the databinding?
Window1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
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;
using System.Threading;
namespace WpfTEST
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window, INotifyPropertyChanged
{
public Window1()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(Window1_Loaded);
this.PropertyChanged += new PropertyChangedEventHandler(Window1_PropertyChanged);
}
public bool Flag
{
get { return m_flag; }
set
{
m_flag = value;
OnPropertyChanged("Flag");
}
}
private bool m_flag = false;
void Window1_Loaded(object sender, RoutedEventArgs e)
{
this.m_cbox.DataContext = this;
for (int i = 0; i < 1000; i++)
{
Flag = (i % 2 == 0);
Thread.Sleep(200);
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
void Window1_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
}
#endregion
}
}
Window1.xaml
<Window x:Class="WpfTEST.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300" x:Name="window">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*" />
<ColumnDefinition Width="0.5*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0.5*" />
<RowDefinition Height="0.5*" />
</Grid.RowDefinitions>
<CheckBox x:Name="m_cbox" Content="Let's see what happens" Grid.Row="2" Grid.Column="2" Grid.RowSpan="1" Grid.ColumnSpan="1" IsChecked="{Binding Path=Flag, ElementName=window, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</Window>
The only real problem I see with your code is the sleep loop in Loaded. That causes the UI thread to go unresponsive for the duration of the loop, since you're putting the UI thread into a blocked state.
There's a better way to test if you can change the flag from code and have your binding fire. Add a button to the window, hook up a click handler to the button, and in that click handler, toggle Flag -- I made those modifications to your original code (and removed the sleep loop), clicking on the button toggles the checkbox's state in the fashion you seem to desire.
<Window x:Class="WpfTEST.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300" x:Name="window">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*" />
<ColumnDefinition Width="0.5*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0.5*" />
<RowDefinition Height="0.5*" />
</Grid.RowDefinitions>
<!-- I added this button: -->
<Button Click="Button_Click" Grid.Row="0" Grid.Column="0"/>
<CheckBox x:Name="m_cbox" Content="Let's see what happens" Grid.Row="2" Grid.Column="2" Grid.RowSpan="1" Grid.ColumnSpan="1" IsChecked="{Binding Path=Flag, ElementName=window, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</Window>
and in code behind:
using System;
using System.Collections.Generic;
using System.ComponentModel;
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;
using System.Threading;
namespace WpfTEST {
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window, INotifyPropertyChanged {
public Window1() {
InitializeComponent();
this.Loaded += new RoutedEventHandler(Window1_Loaded);
this.PropertyChanged += new PropertyChangedEventHandler(Window1_PropertyChanged);
}
public bool Flag {
get { return m_flag; }
set {
m_flag = value;
OnPropertyChanged("Flag");
}
}
private bool m_flag = false;
void Window1_Loaded( object sender, RoutedEventArgs e ) {
this.m_cbox.DataContext = this;
Flag = false;
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged( string name ) {
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
void Window1_PropertyChanged( object sender, PropertyChangedEventArgs e ) {
}
#endregion
private void Button_Click( object sender, RoutedEventArgs e ) {
Flag = !Flag;
}
}
}
Other than the above modifications to add the button and its click handler, and remove that sleep loop, I made no modifications to your original code (although you could streamline it a bit).
That will only work if your Flag property is a dependency property. To get this working in the simplest way i can think of i would do the following :
change your binding to be :
IsChecked="{Binding Path=Flag,Mode=TwoWay}"
and in your constructor in your code behind do this :
DataContext = this;
or in XAML
DataContext="{Binding RelativeSource={RelativeSource self}}"
Note : Flag does not need to be a dependency property if you are setting the datacontext to be the window.