Wpf - bestway to register a change on property - wpf

Whatsup, I'm new to wpf technology and im having trouble to find out how to popup a message on screen when there is a change on the User property. (Except that, the code works perfect).
*My goal is to register an exsisting event that takes care of it and NOT to do it in the MyData class by rewriting the 'Set'.
xaml:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<StackPanel x:Name="MyGrid1">
<StackPanel.Resources>
<local:MyData x:Key="mySource1"
User="Arik2" />
</StackPanel.Resources>
<TextBox x:Name="target1"
Grid.Row="1"
Grid.Column="2"
Text="{Binding Source={StaticResource mySource1}, Path=User,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox x:Name="target2"
Grid.Row="2"
Grid.Column="2"
Text="{Binding Source={StaticResource mySource1}, Path=User,Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Window>
Thats my app code:
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class MyData
{
public string User { get; set; }
}
}
Thank you all.

For the textboxes you have you can always open a message window in a textchanged event handler.
For example
<TextBox TextChanged="txt_TextChanged" >
Private Sub txt_TextChanged(sender As System.Object, e As System.Windows.Controls.TextChangedEventArgs)
MessageBox.Show("Value changed")
End Sub
Note this is probably what you are looking for but is triggering off the text in the box changing not the property itself. If you use anything like validation and input invalid data this event will fire but the text will not change.
I think you can use the sourcechanged event to get exactly what you want.

Related

Getting weird behaviours when changing the binding to the TreeView

I have ObservableCollection Fathers which contains property ObservableCollection Sons.
And I'm displaying it on the TreeView setting its DataContext property.
The Sons property displays as a ListBox of radio button under each Father - binded to ItemsSource.
First time setting the DataContext of the tree view to the fathers list, everything is working good. The radio buttons are checked according to the data.
Now, I'm setting the TreeView.DataContext to null - so the data will disappear. and then back to the original Fathers ObservableCollection which I set in the first time.
And now from some reason the radio buttons stopped being synchronized with the son object.
And I got deeper and I saw that the setter in the son object (that binded to the radio button) is raised with false from some reason. I Guess something related to the binding.
Is there any cache that the TreeView, or the ObservableCollection is saving after binding ? I want it to work like the first time I set the bind - Which there like it should only the getter is being called like it should.
Thanks.
This is my Tree view
<UserControl x:Class="Tester.CTLMyTree"
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:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<Border Background="#FF919191" BorderThickness="1" CornerRadius="5"
HorizontalAlignment="Left" VerticalAlignment="Top"
Padding="5" BorderBrush="Black" Height="207" Width="190">
<Border.Resources>
<sdk:HierarchicalDataTemplate x:Key="LayerListTemplate">
<StackPanel Orientation="Vertical" Width="200" >
<TextBlock Text="Hello"/>
<ListBox x:Name="lstViews" ItemsSource="{Binding Sons}" BorderThickness="0" Width="200">
<ListBox.ItemTemplate>
<DataTemplate>
<RadioButton Content="Check" IsChecked="{Binding IsChecked, Mode=TwoWay}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</sdk:HierarchicalDataTemplate>
</Border.Resources>
<sdk:TreeView ItemsSource="{Binding}" ItemTemplate="{StaticResource LayerListTemplate}" x:Name="myTreeView" />
</Border>
</Grid>
</UserControl>
The Objects behind
public class CCFather
{
public CCFather()
{
Sons = new ObservableCollection<CCSon>();
}
public ObservableCollection<CCSon> Sons
{
get;
set;
}
}
public class CCSon
{
private bool m_blnChecked;
public bool IsChecked
{
get
{
return m_blnChecked;
}
set
{
m_blnChecked = value;
}
}
}
In my application i added this treeview control and called it m_objSimpleTree.
This code is the initializing
m_objItems = new ObservableCollection<CCFather>();
CCFather objItem1 = new CCFather();
objItem1.Sons.Add(new CCSon());
objItem1.Sons[0].IsChecked = true;
m_objItems.Add(objItem1);
m_objSimpleTree.myTreeView.DataContext = m_objItems;
And when i press a button i'm doing this
m_objSimpleTree.myTreeView.DataContext = null;
m_objSimpleTree.myTreeView.DataContext = m_objItems;
This code will raise already the IsChecked setter of the son to false (Why ???)
But the RadioButton will still be checked.
Second time pressing the button. it will be unchecked and the setter didn't raise.
When i'm pressing on the radio button It's raising twice the setter. First time with false
second with true.
Can't figure why it's happening.. The only think i can think of is that the treeview is saving something in the first binding or something like this.
It does so because you have used twoWay binding for the control
binding code project
In a two way binding when you change some thig on the view then the data gets saved in the object also . to which the datacontext is assigned. try oneWay for that . But need to be careful as if you wanna save data using twoWay one way might not help. MVVM suggests to use two way binding to save data but you want a refreshed list then create a new object :)
Also try clear Binding
Clear binding
Am not sure of the last link as never tried. Please o through it might get an idea. But since you might need a new object again so you can create a fresh object to assign to datacontext.
--- EDIT-----
Here's a xaml code
<UserControl
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:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" x:Class="SilverlightSOApp.MainPage"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<Border Background="#FF919191" BorderThickness="1" CornerRadius="5"
HorizontalAlignment="Left" VerticalAlignment="Top"
Padding="5" BorderBrush="Black" Height="207" Width="190">
<Border.Resources>
<sdk:HierarchicalDataTemplate x:Key="LayerListTemplate">
<StackPanel Orientation="Vertical" Width="200" >
<TextBlock Text="Hello"/>
<ListBox x:Name="lstViews" ItemsSource="{Binding Sons}" BorderThickness="0" Width="200">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<RadioButton Content="Check" GroupName="abcd" IsChecked="{Binding IsChecked, Mode=TwoWay}"/>
<RadioButton Content="Check" GroupName="abcd" IsChecked="{Binding IsChecked2, Mode=TwoWay}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</sdk:HierarchicalDataTemplate>
</Border.Resources>
<sdk:TreeView ItemsSource="{Binding}" ItemTemplate="{StaticResource LayerListTemplate}" x:Name="myTreeView" />
</Border>
<Button Content="Button" HorizontalAlignment="Left" Margin="303,268,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
</Grid>
And the c#
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Browser;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace SilverlightSOApp
{
public partial class MainPage : UserControl
{
private ObservableCollection<CCFather> m_objItems;
public MainPage()
{
InitializeComponent();
Loaded += new RoutedEventHandler(MainPage_Loaded);
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
m_objItems = new ObservableCollection<CCFather>();
CCFather objItem1 = new CCFather();
objItem1.Sons.Add(new CCSon());
objItem1.Sons[0].IsChecked = false;
m_objItems.Add(objItem1);
myTreeView.DataContext = m_objItems;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
myTreeView.DataContext = null;
myTreeView.DataContext = m_objItems;
}
}
public class CCFather
{
public CCFather()
{
Sons = new ObservableCollection<CCSon>();
}
public ObservableCollection<CCSon> Sons
{
get;
set;
}
}
public class CCSon
{
private bool m_blnChecked;
private bool m_blnChecked2;
public bool IsChecked
{
get
{
return m_blnChecked;
}
set
{
m_blnChecked = value;
}
}
public bool IsChecked2
{
get
{
return m_blnChecked2;
}
set
{
m_blnChecked2 = value;
}
}
}
}
Now the main point if you want to implement it for single radio button then you need to implement the Click event and set radio button to false and next time to true :) or else you need to use a checkbox one radio button once checked cannot be converted to false

How do I bind a WPF control to a VB.net property? [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Data Binding WPF Property to Variable
How would I bind my module1 property to my WPF TextBox1?
WPF code:
<Window x:Class="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 Height="23" HorizontalAlignment="Left" Margin="210,146,0,0" Name="TextBox1" VerticalAlignment="Top" Width="120" />
</Grid>
</Window>
VB.net code:
Module Module1
ReadOnly Property tbBinding As String
Get
Return "Success!"
End Get
End Property
End Module
Below is code that I have been working on based on the feed back I have been getting and the reading I have been doing.
/#######Current code in progres (trying with a class instead of a module)#######/
XAML:
<Window x:Class="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 DataContext="Class1">
<TextBox Height="23" HorizontalAlignment="Left" Margin="210,146,0,0" Name="TextBox1" VerticalAlignment="Top" Width="120" Text="{Binding Path=tbBinding2}"/>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="192,74,0,0" Name="Button1" VerticalAlignment="Top" Width="75" />
</Grid>
</Window>
Class1:
Imports System.ComponentModel
Public Class Class1
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private Sub NotifyPropertyChanged(ByVal info As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
End Sub
Dim varField As String = String.Empty
Public Property tbBinding2 As String
Get
Return varField
End Get
Set(value As String)
varField = value
NotifyPropertyChanged("tbBinding2")
End Set
End Property
End Class
MainWindow:
Class MainWindow
Private Sub Button1_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles Button1.Click
Dim myClass1 As New Class1
myClass1.tbBinding2 = "Success!"
End Sub
End Class
You are not setting the DataContext anywhere
WPF has two layers: the data layer and the UI layer. The data layer is null by default, and you can set it by setting the DataContext property of any UI objects. Bindings are used to pull data from the data layer into the UI layer.
So, if you say MainWindow.DataContext = new Class1(), then you are setting the data layer beind MainWindow to a new instance of your Class1 object.
Writing <TextBox Text="{Binding tbProperty}" /> in the XAML is telling WPF to look in the data layer for a property called tbProperty and use it for the Text value of the TextBox.
If you change the tbProperty in your Class1 object being used as the DataContext, that change will also be reflected in TextBox.Text (providing you implemented INotifyPropertyChanged). And if the binding mode is set to TwoWay (default for TextBox.Text), then changes to TextBox.Text will also update the tbProperty in the data layer.
I actually recently posted an overview of the DataContext on my blog if you're interested.
You need to implement the INotifyPropertyChanged interface. Refer to this article for an example.
Edit:
Here is an example of binding to class Class1 (a.k.a. the "view model") from XAML:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModels:"clr-namespace:MyApplication.ViewModels"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<viewModels:Class1 />
</Window.DataContext>
<Grid>
<TextBox Height="23" HorizontalAlignment="Left" Margin="210,146,0,0" Name="TextBox1" VerticalAlignment="Top" Width="120" Text="{Binding Path=tbBinding2}"/>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="192,74,0,0" Name="Button1" VerticalAlignment="Top" Width="75" />
</Grid>
</Window>
Note that this XAML assumes that the Class1 class is contained in the same assembly under the MyApplication.ViewModels namespace.

WPF databinding: Why doesn't my copy display on the screen?

What simple thing am I missing here? Why doesn't my copy display on the screen?
<Window x:Class="DeleteThis.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>
<StackPanel>
<TextBlock Text="{Binding Path=SomeCopy}" Height="35" Width="100" Margin="10"/>
</StackPanel>
</Grid>
and my code-behind.
public partial class MainWindow : Window
{
private string _someCopy;
public string SomeCopy
{
get
{
return _someCopy;
}
set
{
_someCopy = value;
}
}
public MainWindow()
{
InitializeComponent();
SomeCopy = "why doesn't this display";
}
}
You never set the DataContext of the Window. Change your XAML to this...
<TextBlock Text="{Binding}" Height="35" Width="100" Margin="10"/>
...and change your code behind to add the DataContext line...
public MainWindow()
{
InitializeComponent();
SomeCopy = "why doesn't this display";
this.DataContext = SomeCopy;
}
Your current issue has nothing to do with needing a DependencyProperty as mentioned in the other answers.
WPF never finds out that the property changed.
To fix it, you can turn the property into a dependency property.
EDIT: You also need to bind to the property on the Window itself, like this
<TextBlock Text="{Binding Path=SomeCopy, RelativeSource={RelativeSource Self}}" ... />
SLaks's answer is the correct one. But making dependency properties manually is annoying, so I link you to my favorite solution: A custom PostSharp NotifyPropertyChangedAttribute that, when used in conjunction with PostSharp, makes all the properties of any given class into dependency properties.

WPF Simple Binding to an objects property

Im having some problems with binding in wpf/xaml. Have this simple file:
<Window x:Class="test.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">
<Grid>
<TextBlock Height="21" Foreground="Black" Margin="74,98,84,0" Name="textBlock1" VerticalAlignment="Top" Text="{Binding MyText}" />
</Grid>
</Window>
Where i want to bind the content of the textblock to my property "MyText". My code looks like this:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
public string MyText
{
get { return "This is a test"; }
}
}
All in all very simple, but when i start the textblock has no content - howcome?
you need an element name in your binding:
<Window ... x:Name="ThisWindow"...>
<TextBlock ... Text="{Binding MyText, ElementName=ThisWindow}" />
If I'm remembering my WPF binding syntax correctly, I believe your binding expression should read Text="{Binding Path=MyText}"
There are a number of ways to accomplish this. Probably the easiest for something as simple as this form is:
public Window1()
{
InitializeComponent();
this.DataContext = this;
}

WPF -- Anyone know why I can't get this binding to reference?

<StackPanel x:Name="stkWaitingPatients" Width="300" Margin="0,0,0,-3"
DataContext="{Binding Mode=OneWay, Source={StaticResource local:oPatients}}">
I'm getting StaticResource reference 'local:oPatients' was not found.
Here is the codebehind:
public partial class MainWindow : Window
{
ListBox _activeListBox;
clsPatients oPatients;
public MainWindow()
{
oPatients = new clsPatients(true);
...
To be able to address the object as a StaticResource, it needs to be in a resource dictionary. However, since you're creating the object in MainWindow's constructor, you can set the DataContext in the code-behind like so.
oPatients = new clsPatients(true);
stkWaitingPatients.DataContext = oPatients;
And then change the Binding to this:
{Binding Mode=OneWay}
This is an ok practice if you're not going to be changing the DataContext again, otherwise you'd want a more flexible solution.
Edit: You mentioned ObjectDataProvider in your comment. Here's how you'd do that. First, add an xmlns:sys to the Window for the System namespace (I'm assuming you already have one for xmlns:local):
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Then you can add an ObjectDataProvider to your resource dictionary like this:
<Window.Resources>
<ObjectDataProvider
x:Key="bindingPatients"
ObjectType="{x:Type local:clsPatients}">
<ObjectDataProvider.ConstructorParameters>
<sys:Boolean>True</sys:Boolean>
</ObjectDataProvider.ConstructorParameters>
</ObjectDataProvider>
</Window.Resources>
And refer to it in a Binding with the StaticResource markup like this, using the same string we specified in the x:Key attached property we gave it in the dictionary:
{Binding Source={StaticResouce bindingPatients}, Mode=OneWay}
Edit 2: Ok, you posted more code in your answer, and now I know why it's throwing an exception during the constructor. You're attempting to do this...
lstWaitingPatients.DataContext = oPatients;
... but lstWaitingPatients doesn't actually exist until after this.InitializeComponent() finishes. InitializeComponent() loads the XAML and does a bunch of other things. Unless you really need to do something before all of that, put custom startup code after the call to InitalizeComponent() or in an event handler for Window's Loaded event.
The following sets the ItemsSource in Code Behind and correctly handles the DataBinding:
public partial class MainWindow : Window
{
public MainWindow()
{
clsPatients oPatients = new clsPatients(true);
//assuming oPatients implements IEnumerable
this.lstWaitingPatients.ItemsSource = oPatients;
And the XAML:
<ListBox x:Name="lstWaitingPatients"
IsSynchronizedWithCurrentItem="true"
ItemTemplate="{StaticResource WaitingPatientsItemTemplate}"
FontSize="21.333" Height="423.291"
ScrollViewer.VerticalScrollBarVisibility="Visible"
GotFocus="lstWaitingPatients_GotFocus"
/>
Now, I can't get this to work...I get a general Windows startup error.
Here is the codebehind with the Initializer and the class being instantiated:
public partial class MainWindow : Window
{
ListBox _activeListBox;
public MainWindow()
{
clsPatients oPatients = new clsPatients(true);
lstWaitingPatients.DataContext = oPatients;
this.InitializeComponent();
Here's the top of my XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Orista_Charting"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
x:Class="Orista_Charting.MainWindow"
x:Name="windowMain"
Title="Orista Chart"
Width="1024" Height="768" Topmost="True" WindowStartupLocation="CenterScreen" Activated="MainWindow_Activated" >
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/ButtonStyles.xaml"/>
<ResourceDictionary Source="Resources/OtherResources.xaml"/>
<ResourceDictionary Source="Resources/TextBlockStyles.xaml"/>
<ResourceDictionary Source="Resources/Converters.xaml"/>
</ResourceDictionary.MergedDictionaries>
Here's the pertinent XAML, as you see, I went ahead and moved the DataContext down to the ListBox from the StackPanel. This doesn't run, but it does render in Design View (however, with no data present in the ListBox):
<!-- Waiting Patients List -->
<Border BorderThickness="1,1,1,1" BorderBrush="#FF000000" Padding="10,10,10,10"
CornerRadius="10,10,10,10" Background="#FFFFFFFF" Margin="15.245,187.043,0,41.957" HorizontalAlignment="Left" >
<StackPanel x:Name="stkWaitingPatients" Width="300" Margin="0,0,0,-3">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Waiting Patients:" VerticalAlignment="Center" FontSize="21.333" Margin="0,0,0,20"/>
<TextBlock HorizontalAlignment="Right" Margin="0,0,38.245,0" Width="139" Height="16"
Text="Minutes Waiting" TextWrapping="Wrap" Foreground="#FF9C2525" FontWeight="Bold" VerticalAlignment="Bottom"
TextAlignment="Right"/>
<!-- Too be implemented, this is the wait animation -->
<!--<Image x:Name="PollGif" Visibility="{Binding Loading}"
HorizontalAlignment="Left" Margin="100,0,0,0" Width="42.5" Height="42.5"
Source="Images/loading-gif-animation.gif" Stretch="Fill"/>-->
</StackPanel>
<ListBox x:Name="lstWaitingPatients"
DataContext="{Binding Mode=OneWay}" ItemsSource="{Binding Mode=OneWay}"
IsSynchronizedWithCurrentItem="true"
ItemTemplate="{StaticResource WaitingPatientsItemTemplate}"
FontSize="21.333" Height="423.291" ScrollViewer.VerticalScrollBarVisibility="Visible"
GotFocus="lstWaitingPatients_GotFocus"
/>
</StackPanel>
</Border>
Ok, but if I just take comment out the assigment line in the codebehind, it does run (albeit with no data in the listbox):
public partial class MainWindow : Window
{
ListBox _activeListBox;
public MainWindow()
{
clsPatients oPatients = new clsPatients(true);
//lstWaitingPatients.DataContext = oPatients;
THANKS!

Resources