How to create a custom Textbox in WPF? - wpf

I am new to wpf
I want to create a custom textbox with the features such as
Change background color or Change Border Darker/Lighter on GotFocus and LostFocus.
Write only in UPPER CASE
WaterMark
Rounded Corner
I found code for rounded corner in XAML and it works good.
Following is the code :
<TextBox x:Name="txtUserName" BorderThickness="1" Height="23">
<TextBox.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="3"/>
</Style>
</TextBox.Resources>
</TextBox>
I use to code in VB.NET Winforms before and I did something like this in there.
I made a class say MainTextBoxex
Public Class MainTextboxex
Inherits TextBox
Private Sub MainTextBoxEx_Enter(sender As Object, e As EventArgs) Handles Me.Enter
Me.BackColor = Color.FromArgb(247, 180, 65)
'Me.SelectAll()
End Sub
Private Sub MainTextBoxEx_Leave(sender As Object, e As EventArgs) Handles Me.Leave
Me.BackColor = Color.White
End Sub
Public Sub New()
MyBase.New()
Me.CharacterCasing = CharacterCasing.Upper
End Sub
Private Sub MainTextboxex_KeyPress(sender As Object, e As KeyPressEventArgs) Handles Me.KeyPress
If e.KeyChar = ChrW(39) Then e.KeyChar = ChrW(96)
End Sub
Private NotInheritable Class NativeMethods
Private Sub New()
End Sub
Private Const ECM_FIRST As UInteger = &H1500
Friend Const EM_SETCUEBANNER As UInteger = ECM_FIRST + 1
<DllImport("user32.dll", EntryPoint:="SendMessageW")>
Public Shared Function SendMessageW(hWnd As IntPtr, Msg As UInteger, wParam As IntPtr, <MarshalAs(UnmanagedType.LPWStr)> lParam As String) As IntPtr
End Function
End Class
Private _watermark As String
Public Property Watermark() As String
Get
Return _watermark
End Get
Set(value As String)
_watermark = value
UpdateWatermark()
End Set
End Property
Private Sub UpdateWatermark()
If IsHandleCreated AndAlso _watermark IsNot Nothing Then
NativeMethods.SendMessageW(Handle, NativeMethods.EM_SETCUEBANNER, CType(1, IntPtr), _watermark)
End If
End Sub
Protected Overrides Sub OnHandleCreated(e As EventArgs)
MyBase.OnHandleCreated(e)
UpdateWatermark()
End Sub
End Class
This worked awesome in winforms as I needed. (No rounded corners in there)
I just need to know how it is done here.
I want something that I don't have to code it everytime or for every Individual TextBox for the above feature everywhere.
Just use this custom TextBox and all set.
This will help me alot.
Thank You.

Use UserControls and CustomControls depending on how much deep you want to dive in your customization.
You can add your own properties by using "Dependency Property", and about the events you can expose Action Events and pass them through your Controls Action events.

Related

Equivalent to Microsoft Forms 2.0 ComboBox .TopIndex property in Windows Forms and Access Forms?

The (deprecated) Microsoft Forms 2.0 controls include a combobox which provides an invaluable property: .TopIndex (documentation here).
It seems that this property is not available with standard comboboxes in forms in Microsoft Access 2019 (according to the documentation), and is not available with standard comboboxes in Windows Forms (.NET) (here, ComboBox inherits from ListControl and does not provide this property, while ListBox also inherits from ListControl, but provides it).
I have a lot of old code which heavily relies on the .TopIndex property, and it's time to move that code to other technologies.
So I'd like to know if I have missed something in the documentation and if there is an equivalent property with another name which I could use to determine which items are visible in the list part of a combobox. I'd like to know this for comboboxes in Access 2019 (I am not that hostile towards this application as many others here are) as well as for comboboxes in Windows Forms.
I am aware that there are a lot of free and commercial controls (including comboboxes) with enhanced functionality for Windows Forms. I will definitely go that way (or write my own) unless I have missed something in the documentation.
However, the situation is completely different when it comes to Access 2019 forms. I could not find a single free third-party ActiveX / COM combobox which I could use on Access forms and which provides this functionality. Theoretically, I probably could write an ActiveX / COM control using .NET and then use it on Access 2019 forms, but this seems quite painful.
As far as .Net WinForm ComboBox goes, you have not missed anything as the functionality of the TopIndex property is not implemented. That said, it is pretty straight forward to extend the base ComboBox control to add this property. The following example control should get you started.
This control attaches a listener to the native ListBox dropdown and updates the TopIndex property on WM_VSCROLL and the LB_SETCARETINDEX (this captures the initial position on opening) messages. Additionally, the base SelectedIndexChange event is used to capture changes due to keyboard actions (pgUp/pgDn, Arrow up/down). The TopIndex property is retained after the dropdown closes and is reset on opening the dropdown. The control also exposes a TopIndexChanged event.
Imports System.Runtime.InteropServices
Public Class ComboBoxEx : Inherits ComboBox
Private listBoxListener As ListBoxNativeWindow
Public Event TopIndexChanged As EventHandler(Of ComboBoxTopIndexArg)
Private _TopIndex As Int32 = -1
Public Sub New()
MyBase.New
listBoxListener = New ListBoxNativeWindow(Me)
End Sub
Public Property TopIndex As Int32
Get
Return _TopIndex
End Get
Private Set(value As Int32)
If value <> _TopIndex Then
_TopIndex = value
RaiseEvent TopIndexChanged(Me, New ComboBoxTopIndexArg(value))
End If
End Set
End Property
Protected Overrides Sub OnDropDown(e As EventArgs)
_TopIndex = -1 ' reset on opening the listbox
MyBase.OnDropDown(e)
End Sub
Private Class ListBoxNativeWindow : Inherits NativeWindow
Private listBoxHandle As IntPtr
Private TopIndex As Int32
Private parent As ComboBoxEx
Public Sub New(ByVal parent As ComboBoxEx)
Me.parent = parent
WireParent()
If parent.IsHandleCreated Then
GetListBoxHandle()
AssignHandle(listBoxHandle)
End If
End Sub
Private Sub WireParent()
AddHandler parent.HandleCreated, AddressOf Me.OnHandleCreated
AddHandler parent.HandleDestroyed, AddressOf Me.OnHandleDestroyed
AddHandler parent.SelectedIndexChanged, AddressOf UpdateTopIndexOnIndexChanged
End Sub
Private Sub OnHandleCreated(ByVal sender As Object, ByVal e As EventArgs)
GetListBoxHandle()
AssignHandle(listBoxHandle)
End Sub
Private Sub OnHandleDestroyed(ByVal sender As Object, ByVal e As EventArgs)
ReleaseHandle()
End Sub
Private Sub UpdateTopIndexOnIndexChanged(sender As Object, e As EventArgs)
SetParentTopIndex()
End Sub
Private Sub GetListBoxHandle()
Const CB_GETCOMBOBOXINFO As Int32 = &H164
Dim info As New ComboBoxInfo
info.cbSize = Marshal.SizeOf(info)
Dim res As Boolean = SendMessage(Me.parent.Handle, CB_GETCOMBOBOXINFO, Nothing, info)
listBoxHandle = info.hwndList
End Sub
Protected Overrides Sub WndProc(ByRef m As Message)
Const WM_VSCROLL As Int32 = &H115
Const LB_SETCARETINDEX As Int32 = &H19E
MyBase.WndProc(m)
If m.Msg = WM_VSCROLL OrElse m.Msg = LB_SETCARETINDEX Then
SetParentTopIndex()
End If
End Sub
Private Sub SetParentTopIndex()
Const LB_GETTOPINDEX As Int32 = &H18E
parent.TopIndex = SendMessage(listBoxHandle, LB_GETTOPINDEX, IntPtr.Zero, IntPtr.Zero)
End Sub
End Class
Public Class ComboBoxTopIndexArg : Inherits EventArgs
Public Sub New(topIndex As Int32)
Me.TopIndex = topIndex
End Sub
Public ReadOnly Property TopIndex As Int32
End Class
#Region "NativeMethods"
<StructLayout(LayoutKind.Sequential)>
Private Structure ComboBoxInfo
Public cbSize As Int32
Public rcItem As RECT
Public rcButton As RECT
Public stateButton As IntPtr
Public hwndCombo As IntPtr
Public hwndEdit As IntPtr
Public hwndList As IntPtr
End Structure
<StructLayout(LayoutKind.Sequential)>
Private Structure RECT
Public Left, Top, Right, Bottom As Int32
End Structure
<DllImport("user32.dll")>
Private Shared Function SendMessage(hWnd As IntPtr, Msg As Int32, wParam As IntPtr, <Out()> ByRef lParam As ComboBoxInfo) As Boolean
End Function
<DllImport("user32.dll")>
Private Shared Function SendMessage(hWnd As IntPtr, Msg As Int32, wParam As IntPtr, lParam As IntPtr) As Int32
End Function
#End Region
End Class
I leave it to you to wrap this in an ActiveX exposed wrapper for use in Access. Doing so is fairly easy using the templates in the Microsoft InteropForms Toolkit 2.1. Just note that those templates are setup using the "Any CPU" platform and you will need to change that to "x86".

Mouse double click windowsformhost on a usercontrol with a spreadsheet

I have a usercontrol that contains a spread and I have this methods
Public Event DOBLECLICK()
Public Sub sp1_CellDoubleClick(sender As Object, e As FarPoint.Win.Spread.CellClickEventArgs)
RaiseEvent DOBLECLICK()
End Sub
and in the mainwindow.xaml in the function MainWindow_Loaded I have:
AddHandler host.sp1.CellDoubleClick, AddressOf host.sp1_CellDoubleClick
my question is, how can i use the event of double click and when clicked i hide the windowsformhost i know that i can hide it whit
WinFormsHost.Visibility = Windows.Visibility.Hidden but how to when clicked the doubleclick on the spread.
finally i got it,
in the user control put this:
Public Event Dobleclick()
Private Sub sp1_CellDoubleClick(sender As Object, e As FarPoint.Win.Spread.CellClickEventArgs) Handles spEmpresas.CellDoubleClick
RaiseEvent Dobleclick()
End Sub
and in the MainWindow.xaml:
Imports nameofyourprogram.Control
Public Class MainWindow
Dim host As New nameofyourprogram.Control
Public Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
AddHandler host.Dobleclick, AddressOf doubleclick
end sub
Sub doubleclick()
msgbox("now you can work whit and event in your mainwindow ")
'after this message i want to hide my windownforhost
WinFormsHost.Visibility = Windows.Visibility.Hidden
end sub
End Class

Two-way WPF binding to Properties of a class inside of an ObservableCollection

I've searched as best as I can, and I can't find an answer to this specific problem that I have... WPF binding seems to be great and all, but I end up banging my head against the wall more often than not.
Okay, I have a singleton class which is ultimately the one that I'm binding to:
Imports System.ComponentModel
Imports System.Collections.ObjectModel
Public Class AmandaSeyfried
Implements INotifyPropertyChanged
Shared _config As New config
Public Event PropertyChanged(sender As Object, E As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
Protected Overridable Sub OnPropertyChanged(propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
Private Shared _thisInstance As AmandaSeyfried
Protected Sub New()
' initialization goes here
End Sub
Public Shared Function GetSingleton() As AmandaSeyfried
' initialize object if it hasn't already been done
If _thisInstance Is Nothing Then
_thisInstance = New AmandaSeyfried
End If
' return the initialized instance
Return _thisInstance
End Function
Public Class CountryTranslation
Implements INotifyPropertyChanged
Private Property _englishCountryName As String = ""
Public Property EnglishCountryName As String
Get
Return _EnglishCountryName
End Get
Set(value As String)
If _englishCountryName <> value Then
_englishCountryName = value
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("EnglishCountryName"))
End If
End Set
End Property
Private Property _foreignCountryName As String = ""
Public Property ForeignCountryName As String
Get
Return _foreignCountryName
End Get
Set(value As String)
If _foreignCountryName <> value Then
_foreignCountryName = value
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("ForeignCountryName"))
End If
End Set
End Property
Public Event PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
End Class
Private WithEvents _countryTranslations As New ObservableCollection(Of CountryTranslation)
Public Property CountryTranslations As ObservableCollection(Of CountryTranslation)
Get
If _config.GetKeyTextValue("countryTranslations") <> "" Then
Dim reader As New IO.StringReader(_config.GetKeyTextValue("countryTranslations"))
Dim Serializer As New Xml.Serialization.XmlSerializer(_countryTranslations.GetType)
_countryTranslations = Serializer.Deserialize(reader)
End If
Return _countryTranslations
End Get
Set(value As ObservableCollection(Of CountryTranslation))
_countryTranslations = value
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("CountryTranslations"))
End Set
End Property
Private Sub CountryTranslationCollectionChanged(sender As Object, e As Specialized.NotifyCollectionChangedEventArgs) Handles _countryTranslations.CollectionChanged
Dim newStringWriter As New IO.StringWriter
Dim NewSerializer As New Xml.Serialization.XmlSerializer(_countryTranslations.GetType)
NewSerializer.Serialize(newStringWriter, _countryTranslations)
_config.SaveKeyTextValue("countryTranslations", newStringWriter.ToString)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("CountryTranslations"))
End Sub
End Class
_config is a misnamed helper class that stores and retrieves data from a local SqlCe instance. Essentially the object is serialized, stored in the DB, and then pulled out of the DB any times it's needed and deserialized back into an object. All in all, it seems to be working fairly well.
My problem is that although I can bind to the object, and I can monitor when a row is added in a WPF DataGrid via the CollectionChangedMethod handler, I don't get any notification when either of the two properties of CountryTranslation are changed.
The rest of my related code is... XAML... there's obviously more, but I don't believe the XAML portion of the binding is to blame, so I'll trim it to the relevant:
<toolkit:DataGrid Margin="12,12,12,12" ItemsSource="{Binding Path=KarenSmith.CountryTranslations, Mode=TwoWay}" AutoGenerateColumns="False" HorizontalAlignment="Stretch" Width="Auto" SelectionMode="Single">
<toolkit:DataGrid.Columns>
<toolkit:DataGridTextColumn Width="283" Binding="{Binding EnglishCountryName,Mode=TwoWay}" />
<toolkit:DataGridTextColumn Width="283" Binding="{Binding ForeignCountryName,Mode=TwoWay}" />
</toolkit:DataGrid.Columns>
</toolkit:DataGrid>
And the nice and simple code-behind:
Public Class Preferences
Public Property KarenSmith As AmandaSeyfried = AmandaSeyfried.GetSingleton
Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
DataContext = Me
End Sub
Private Sub Close_Click(sender As System.Object, e As System.Windows.RoutedEventArgs)
Me.Close()
End Sub
End Class
If I throw some break points on the Getter and Setters of the CountryTranslation class, I can monitor when they're being changed (via the datagrid, so binding is working), but try as I might I can't figure out how to raise an event based upon that back in the main class to subsequently update the datastore to show the changes.
Usually I add a CollectionChanged event to the ObservableCollection, which attaches a PropertyChanged event to it's items when they get added, and that event listener listens for changes and would handle them as needed.
Here's an example: (hope the syntax is correct since I just ran it through a C# to VB.Net converter)
Public Sub New()
AddHandler MyCollection.CollectionChanged, AddressOf MyCollection_CollectionChanged
End Sub
Private Sub MyCollection_CollectionChanged(sender As Object, e As CollectionChangedEventArgs)
If e.NewItems IsNot Nothing Then
For Each item As MyItem In e.NewItems
AddHandler item.PropertyChanged, AddressOf MyItem_PropertyChanged
Next
End If
If e.OldItems IsNot Nothing Then
For Each item As MyItem In e.OldItems
RemoveHandler item.PropertyChanged, AddressOf MyItem_PropertyChanged
Next
End If
End Sub
Private Sub MyItem_PropertyChanged(sender As Object, e As PropertyChangedEventArgs)
If e.PropertyName = "Some Property" Then
DoWork()
End If
End Sub
The C# version looks like this:
public MyViewModel()
{
MyCollection.CollectionChanged += MyCollection_CollectionChanged;
}
void MyCollection_CollectionChanged(object sender, CollectionChangedEventArgs e)
{
if (e.NewItems != null)
foreach(MyItem item in e.NewItems)
item.PropertyChanged += MyItem_PropertyChanged;
if (e.OldItems != null)
foreach(MyItem item in e.OldItems)
item.PropertyChanged -= MyItem_PropertyChanged;
}
void MyItem_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Some Property")
DoWork();
}

WPF distinguish between coding-SelectionChanged and the mouse-SelectionChanged

I have a machiavellian question (for me).
In my WPF application I have a ListBox that has in the ItemTemplate a Combobox. When the user select a ComboBoxItem, I have to do some complex operations on the ObservableCollection that is the ItemsSource of the ListBox, then I have to show the ListBox with the changed data. The problem is that if I handle the event "SelectionChanged" of the ComboBox control, every time I modify the source-class of the comboboxItems I enter in the method that handle the event, and this generate wrong results. In short I have to distinguish, in some way, between the SelectionChanged generated by code, and the SelectionChanged generated manually by the user with the mouse.
I have tried many ways, but nothing that works :-(
The soution I thought was the best, is to handle the event "GotFocus" or "MouseUp" of the ContentPresenter of the ItemContainerStyle of the Combo, or else to handle the same events ("GotFocus" and "MouseUp") of the ItemsPanel of the Combo, but the method I handled didn't capture the event (in debug the cursor doesn't enter at all in the method).
I can't use a boolean to stop the method "SelectionChanged" until the "first round" is finished, because the changes of the source-class of the ComboBoxItems occurs after that the method has been all executed.
The default value of the Combos is not always the first (it would be too easy :-)), and not always the same. Everytime the user select an item of one of the Combo, the default value of the other Combos has to change.
Can you help me?
Pileggi
' XAML
<Style x:Key="modComboCriteriEventParts" TargetType="{x:Type ComboBox}">
<EventSetter Event="Selector.SelectionChanged" Handler="cb_SelectionChanged"/>
</Style>
<DataTemplate x:Key="modLBoxCriteriParts">
<ComboBox Style = "{StaticResource modComboCriteriEventParts}"
ItemsSource = "{Binding CriteriItemList}"
ItemContainerStyle = "{DynamicResource modComboContainerParts}"
SelectedIndex = "{Binding valueSelected}" ... />
</DataTemplate>
<ListBox x:Name="lbCriteri" IsSynchronizedWithCurrentItem="True"
ItemsSource = "{Binding CriteriList, Source={StaticResource P_CriteriDataSource}}"
ItemTemplate = "{DynamicResource modLBoxCriteriParts}"
... />
' Code Behind
Private Sub cb_SelectionChanged(ByVal sender As System.Object, ByVal e As SelectionChangedEventArgs)
Dim ri as New RicambiCriteriList() As ObservableCollection(Of P_CriteriItem)
' some complex operations with ri ...
be = BindingOperations.GetBindingExpression(Me.lbCriteri, ListBox.ItemsSourceProperty)
Dim allCriteri As P_Criteri = DirectCast(be.DataItem, P_Criteri)
allCriteri.AddData (ri)
e.Handled = True
End Sub
' Source-Class
Public Class P_Criteri
Private _CriteriList As New ObservableCollection(Of P_CriteriItem)
Public ReadOnly Property CriteriList() As ObservableCollection(Of P_CriteriItem)
Get
CriteriList = _CriteriList
End Get
End Property
Public Sub AddData(ByVal CriteriListPass As ObservableCollection(Of P_CriteriItem))
_CriteriList.Clear()
For Each a As P_CriteriItem In CriteriListPass
_CriteriList.Add(a)
Next
End Sub
End Class
Public Class P_CriteriItem
Implements INotifyPropertyChanged
Public Sub New(ByVal criterioPass As String, ByVal CriteriItemListPass As ObservableCollection(Of P_CriteriItemValore), _
ByVal widthCriteriValuesPass As Double)
Me._criterio = criterioPass
Me._CriteriItemList = CriteriItemListPass
Me._widthCriteriValues = widthCriteriValuesPass
End Sub
Private _criterio As String = ""
Private _CriteriItemList As New ObservableCollection(Of P_CriteriItemValore)
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Property criterio() As String
Get
Return Me._criterio
End Get
Set(ByVal value As String)
If Not Object.Equals(Me._criterio, value) Then
Me._criterio = value
Me.OnPropertyChanged ("criterio")
End If
End Set
End Property
Public Property CriteriItemList() As ObservableCollection(Of P_CriteriItemValore)
Get
Return Me._CriteriItemList
End Get
Set(ByVal value As ObservableCollection(Of P_CriteriItemValore))
If Not Object.Equals(Me._CriteriItemList, value) Then
Me._CriteriItemList = value
Me.OnPropertyChanged ("CriteriItemList")
End If
End Set
End Property
Protected Overridable Sub OnPropertyChanged(ByVal propertyName As String)
Dim handler As PropertyChangedEventHandler = Me.PropertyChangedEvent
If handler IsNot Nothing Then
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End If
End Sub
End Class
Public Class P_CriteriItemValore
Implements INotifyPropertyChanged
Public Sub New(ByVal criterioValorePass As String)
Me._criterioValore = criterioValorePass
End Sub
Private _criterioValore As String = Nothing
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Property criterioValore() As String
Get
Return Me._criterioValore
End Get
Set(ByVal value As String)
If Not Object.Equals(Me._criterioValore, value) Then
Me._criterioValore = value
Me.OnPropertyChanged ("criterioValore")
End If
End Set
End Property
Protected Overridable Sub OnPropertyChanged(ByVal propertyName As String)
Dim handler As PropertyChangedEventHandler = Me.PropertyChangedEvent
If handler IsNot Nothing Then
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End If
End Sub
End Class
Firstly I think its better to handle events on the item container itself and not on the content presenter within the item. And now that I think of it that's probably why you don't see the events. The container is probably eating the events for selection.
But either way if you can't catch the MouseDown/GotFocus events, you can use the PreviewMouseDown/PreviewGotFocus events. Just in case you are not sure what these mean you should read up on wpf event routing architecture and bubbling and tunneling events.

Update UI when property is changed

I am quite new to WPF and I am confused about how Data Bindings should behave.
I have created a class that has 1 property ("status") and three methods that are responsible for changing the status. This class implements the INotifyPropertyChanged interface so that I am able to notify the calling code when the Status changes.
The class looks like this:
Public Class StreetLight
Implements System.ComponentModel.INotifyPropertyChanged
Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
Private _status As String
Public Property Status As String
Get
Return _status
End Get
Set(ByVal value As String)
_status = value
RaiseEvent PropertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs("Satus"))
End Set
End Property
Public Sub New()
_status = "unknown"
End Sub
Public Sub Red()
Status = "Red"
End Sub
Public Sub Yellow()
Status = "Yellow"
End Sub
Public Sub Green()
Status = "Green"
End Sub
End Class
I have created a WPF User Control to represent this class.
This User Control is bound to an instance the StreetLight class. It displays the status of the StreetLight and allows the user to change the status using buttons:
<UserControl x:Class="StreetLightUC"
x:Name="StreetLightUC"
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"
xmlns:twpf="clr-namespace:TryingWPF"
mc:Ignorable="d"
d:DesignHeight="30" d:DesignWidth="360">
<UserControl.Resources>
<twpf:StreetLight x:Key="theLight" PropertyChanged="theLight_PropertyChanged" />
</UserControl.Resources>
<StackPanel x:Name="StreetLightContent" Orientation="Horizontal">
<Label Width="100" HorizontalAlignment="Left">Street Light _Status</Label>
<Label x:Name="streetLightValue" Width="120" HorizontalAlignment="Right" Content="{Binding Path=Status, Mode=OneWay}"></Label>
<Button x:Name="Red" Click="TurnRed" Width="60">Turn Red</Button>
<Button x:Name="Green" Click="TurnGreen" Width="60">Turn Green</Button>
</StackPanel>
</UserControl>
My problem is that even when the status is changed for theLight, it is not updated in the Label that is bound to the Status property unless I create a new StreetLight and set the DataContext to this new instance in the "StreetLight_PropertyChanged" event that handles the PropertyChagned event for the "theLight"
Like so:
Public Class StreetLightUC
Public Sub New()
InitializeComponent()
End Sub
Private Sub TurnRed(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Dim light As StreetLight= CType(FindResource("theLight"), StreetLight)
light.Red()
End Sub
Private Sub TurnGreen(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Dim light As StreetLight = CType(FindResource("theLight"), StreetLight)
light.Unlock()
End Sub
Private Sub theLight_PropertyChanged(ByVal sender As System.Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs)
Dim light As StreetLight = CType(sender, StreetLight )
Dim newLight As New StreetLight
newLight.Status = light.Status
StreetLightContent.DataContext = newLight
End Sub
End Class
Am I doing something wrong?
It doesn't seem like I should have to create a new instance of the class to display the updated status-property when this property is change....
Thanks,
-Frinny
You have a typo ("Satus" instead of "Status"):
RaiseEvent PropertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs("Satus"))
Should be:
RaiseEvent PropertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs("Status"))
With this typo, the binding doesn't see that "Status" has changed, and never updates. If you correct this, the PropertyChanged event will correctly reflect that "Status" has changed.

Resources