TextBox in WPF TreeView not updating source - wpf

I cannot tell if I fundamentally don't understand something here, or have just done something silly.
I have a tree view with two types of templates.
Nodes that are HierarchicalDataTemplate with a TextBlock
Leaves that are DataTemplate with a TextBlock and a TextBox3.
The binding from source is working fine in all cases and reading the Name or Name & Value properties from the underlying classes, and updating if from any changes from the code behind.
Both the TreeNode and TreeLeaf class implement INotifyPropertyChanged
However the binding of the TextBox.text property back to the TreeLeaf.Value property (with its getter) does not seem to work.
XAML
<TreeView Name="ItemsTree" Grid.Row="1" Grid.Column="1" ItemsSource="{Binding}">
<TreeView.Resources>
<Color x:Key="detailMark">#FFA1A9B3</Color>
<SolidColorBrush x:Key="detailMarkBrush" Color="{StaticResource ResourceKey=detailMark}" />
<Style x:Key="flatTextBox" TargetType="{x:Type TextBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Grid>
<Rectangle Stroke="{StaticResource ResourceKey=detailMarkBrush}" StrokeThickness="0"/>
<TextBox Margin="1" Text="{TemplateBinding Text}" BorderThickness="0"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<HierarchicalDataTemplate DataType="{x:Type local:TreeNode}" ItemsSource="{Binding Children, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}">
<TextBlock Text="{Binding Name, NotifyOnSourceUpdated=True}">
</TextBlock>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:TreeLeaf}" >
<StackPanel Orientation="Horizontal">
<TextBlock Width="150" Text="{Binding Name, NotifyOnSourceUpdated=True}"/>
<TextBox Width="150" Text="{Binding Value, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Style="{StaticResource ResourceKey=flatTextBox}"/>
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
And then code behind for adding the items to the tree
ItemsTree.Items.Add(treeRoot)
The TreeLeaf class
Public Class TreeLeaf
Implements INotifyPropertyChanged, IEditableObject
Private m_Key As String
Private m_Value As String
Private m_Parent As TreeNode
Private temp_Item As TreeLeaf = Nothing
Private m_Editing As Boolean = False
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Sub New()
m_Key = ""
m_Value = ""
End Sub
Public Sub NotifyPropertyChanged(ByVal propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
Public Sub BeginEdit() Implements IEditableObject.BeginEdit
If Not m_Editing Then
temp_Item = MemberwiseClone()
m_Editing = True
End If
End Sub
Public Sub EndEdit() Implements IEditableObject.EndEdit
If m_Editing = True Then
temp_Item = Nothing
m_Editing = False
End If
End Sub
Public Sub CancelEdit() Implements IEditableObject.CancelEdit
If m_Editing = True Then
m_Key = temp_Item.m_Key
m_Value = temp_Item.m_Value
m_Editing = False
End If
End Sub
Public Property Parent As TreeNode
Get
Return m_Parent
End Get
Set(value As TreeNode)
m_Parent = value
NotifyPropertyChanged("Parent")
End Set
End Property
Public ReadOnly Property Name As String
Get
Return m_Key & " : "
End Get
End Property
Public Property Key As String
Get
Return m_Key
End Get
Set(ByVal value As String)
If Not value = m_Key Then
m_Key = value
NotifyPropertyChanged("Key")
End If
End Set
End Property
Public Property Value As String
Get
Return m_Value
End Get
Set(ByVal value As String)
If Not value = m_Value Then
m_Value = value
NotifyPropertyChanged("Value")
End If
End Set
End Property
End Class
I have seen mentions of setting the DataContext but I don't understand how that would apply to this situation.

Well, unfortunately figured it out.
The style template for the text box was overriding the binding.
The line with TemplateBinding seems to default to a one way from source
<TextBox Margin="1" Text="{TemplateBinding Text}" BorderThickness="0"/>
and it now works if you change it too
<TextBox Margin="1" Text="{Binding Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}" BorderThickness="0"/>

It's not possible to tell from your code whether the binding works or not since the ViewModel code is complicated enough and also depends on the treeview viewmodel code etc...
One way to debug these types of problems is to create a Converter and make it part of the binding that should be debugged. Setting a breakpoint within the Converter will show whether the binding is called as expected when the value in the source or destination has changed. The code for the converter is realy simple:
Imports System
Imports System.Globalization
Imports System.Windows.Data
Namespace MyApp.Converters
Public Class DebugConverter
Inherits IValueConverter
Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As CultureInfo) As Object
Return value
End Function
Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As CultureInfo) As Object
Return value
End Function
End Class
End Namespace
Its integration into a questionable binding is also simple. Add it via its namespace (eg MyApp.Converters) into the XAML:
xmlns:conv="clr-namespace:MyApp.Converters"
...and create an instance via this statement in the Resource section of your TreeView code listing:
<conv:DebugConverter x:Key="DebugConverter"/>
...and last but not least integrate the converter into a binding you want to debug:
<TextBox
Width="150"
Style="{StaticResource ResourceKey=flatTextBox}"
Text="{Binding Value, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, Converter={StaticResource DebugConverter}}" />
Now, when you set a breakpoint in the Convert and ConvertBack method you should see these methods being invoked when you start the application. If you do not see it in either direction you know the binding is either not working or the value is not being updated and you can then search further for the cause.
Hope this helps.

Related

WPF - Combobox does not show selecteditem

Im new to WPF and VB and im having some trouble figuring out why the thing i select in a ComboBox dropdown does not show in the ComboBox after selection.
I have my ComboBox populated through bindings and DataContext. This is my Settings.xaml file
<Window.Resources>
<DataTemplate x:Key="TabList">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Header}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
...
<ComboBox x:Name="tabs"
ItemsSource="{Binding tabList}"
ItemTemplate="{StaticResource ResourceKey=TabList}"
Height="32" />
The views codebehind file (Settings.xaml.vb) then loads the ComboBoxes content in the class' constructor, and the data does show in the ComboBox
Public Class Settings
Private loader As SettingsLoader
Sub New()
InitializeComponent()
Dim sh As New SettingsHandler(True)
loader = New SettingsLoader
loader.tabList = sh.Current.Tabs
DataContext = loader
End Sub
End Class
The SettingsLoader class looks like so. TRTab is my own class that simply inherits from TabItem and only adds a few extra properties, nothing fancy
Public Class SettingsLoader
Private _tabs As List(Of TRTab)
Public Property tabList() As List(Of TRTab)
Get
Return _tabs
End Get
Set(value As List(Of TRTab))
_tabs = value
End Set
End Property
End Class
Do i need to add a property to my SettingsLoader that holds the selected item for the ComboBox to show or what am i missing ?
EDIT: Just to clarify what im trying to achieve: I have a TabControl with a number of tabs. Those tabs' Headers needs to be also shown in a ComboBox for selection
Because TabItem is a ContentControl the ComboBox will display its Content when the item is selected. You could confirm this yourself using the following XAML markup:
<Window.Resources>
<DataTemplate x:Key="TabList">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Header}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<StackPanel>
<ComboBox x:Name="tabs" Height="32"
ItemTemplate="{StaticResource TabList}">
<TabItem Content="Content" Header="Header" />
</ComboBox>
</StackPanel>
When you open the dropdown, you will see "Header" but when you select the item and close the dropdown you will see "Content".
It generally doesn't make a whole much sense to set the ItemsSource property of a ComboBox to an IEnumerable of ContentControls. You could bind the ItemsSource to an IEnumerable(Of String) instead. Just add another property to your SettingsLoader class:
Public Class SettingsLoader
Private _tabs As List(Of TRTab)
Public Property tabList() As List(Of TRTab)
Get
Return _tabs
End Get
Set(value As List(Of TRTab))
_tabs = value
End Set
End Property
Public ReadOnly Property tabHeaders() As IEnumerable(Of String)
Get
If _tabs Is Nothing Then
Return Nothing
End If
Return _tabs.Select(Function(x) x.Header.ToString())
End Get
End Property
End Class
<Window.Resources>
<DataTemplate x:Key="TabList">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<StackPanel>
<ComboBox x:Name="tabs"
ItemsSource="{Binding tabHeaders}"
ItemTemplate="{StaticResource ResourceKey=TabList}"
Height="32" />
</StackPanel>
The other option is to set the Content properties of your TRTab objects to the same values ass their Header properties.

Treeview IsSelected with different viewmodel

I have a treeview binded to an Observablecollection of ParentViewModel.
The ParentViewModel contains an Observablecollection of ChildViewModel.
Both this ViewModel implement an IsSelected property. I put it in both of them because the implementation is a little bit different. The program have to behave differently if I press a Parent or Child item.
My problem is that also if I select a Child item, program execute the IsSelected property of the ParentViewModel, not the one of the ChildViewModel.
This is my XAML:
<TreeView Name="MyTreeView"
ItemsSource="{Binding ParentList}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:ParentViewModel}" ItemsSource="{Binding Childs}">
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="{Binding PictureString}" Height="32" Width="32" Margin="0,8,6,4" />
<TextBlock Grid.Column="1" Text="{Binding Name}" FontSize="15" Margin="10" />
</Grid>
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:ChildViewModel}">
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<!--<Image Grid.Column="0" Source="{Binding PictureString}" Height="32" Margin="0,8,6,4" />-->
<TextBlock Grid.Column="1" Text="{Binding Name}" Height="18" FontSize="15" Margin="10"/>
</Grid>
</DataTemplate>
</TreeView.Resources>
</TreeView>
The program never hit the IsSelected setter of the ChildViewModel.
Thanks in advance
EDIT
This is the BaseViewModel:
Public Class InheritableTreeViewItem
Implements INotifyPropertyChanged
Friend m_Name As String
Public Property Name As String
Get
Return m_Name
End Get
Set(value As String)
If (value <> m_Name) Then
m_Name = value
NotifyPropertyChanged("Name")
End If
End Set
End Property
Friend m_IsSelected As Boolean
Public Overridable Property IsSelected As Boolean
Get
Return m_IsSelected
End Get
Set(value As Boolean)
If (value <> m_IsSelected) Then
m_IsSelected = value
NotifyPropertyChanged("IsSelected")
End If
End Set
End Property
Private m_sPictureString As String
Private m_Items As ObservableCollection(Of InheritableTreeViewItem)
Public Property PictureString As String
Get
Return m_sPictureString
End Get
Set(value As String)
If value <> m_sPictureString Then
m_sPictureString = value
NotifyPropertyChanged("PictureString")
End If
End Set
End Property
Public Property Items As ObservableCollection(Of InheritableTreeViewItem)
Get
Return m_Items
End Get
Set(value As ObservableCollection(Of InheritableTreeViewItem))
m_Items = value
End Set
End Property
Sub New(Name As String)
Me.Name = Name
Me.Items = New ObservableCollection(Of InheritableTreeViewItem)
End Sub
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Sub NotifyPropertyChanged(propName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propName))
End Sub
End Class
And this is the ParentViewModel:
Public Class ParentViewModel
Inherits InheritableTreeViewItem
Public Overrides Property IsSelected As Boolean
Get
Return m_IsSelected
End Get
Set(value As Boolean)
If (value <> m_IsSelected) Then
m_IsSelected = value
If value Then
' The function that I want to call When Parent item is selected in the tree
End If
NotifyPropertyChanged("IsSelected")
End If
End Set
End Property
Sub New(Name As String)
MyBase.New(Name)
Me.PictureString = "/Resources/TreeView/Folder.png"
End Sub
End Class
The ChildViewModel is basically identical, it differs only for the function that I call when it is selected. This function modify the content and visibility of some TextBoxes.
I think your problem is at this block :
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
</Style>
</TreeView.ItemContainerStyle>
Because it is a style, I'm not sure that the DataContext is properly inherited. Also why do you redefine this in the HierarchicalDataTemplate? I don't think you need to redefine this style for ChildViewModelItems.
Maybe you could debug your databinding in depth to see what's happening on this property : How to debug data binding.
Also, the plural of Child is Children, not Childs ;)

WPF binding to parent ItemsSource DataContext from DataTemplate item

I have a ViewModel named "A", containing a property of type ObservableCollection(Of ChannelViewModel). The Model is actually inside this ChannelViewModel class and I'm ok with that.
In the View "A", I have a stackpanel with ItemsSource filled with the ObservableCollection. I added a DataTemplate to show a custom control "Channel" instead of the ChannelViewModel string. The Channel custom control needs to display ChannelViewModel data.
The problem is the DataContext of the Channel is not connected properly to each items of the list. I tried a bunch of things and nothing seems to work. I would appreciate some help please!
Here is the "A" View code:
<Window.DataContext>
<ctrls:AViewModel/>
</Window.DataContext>
<StackPanel x:Name="uiStack" Orientation="Horizontal">
<ItemsControl ItemsSource="{Binding Channels}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ctrls:Channel DataContext="{Binding DataContext}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</StackPanel>
Here is "A" ViewModel :
Private aChannels As New ObservableCollection(Of ChannelViewModel)
Property Channels As ObservableCollection(Of ChannelViewModel)
Get
Return aChannels
End Get
Set(value As ObservableCollection(Of ChannelViewModel))
aChannels = value
OnPropertyChanged("Channels")
End Set
End Property
Public Sub New()
AddChannels()
OnPropertyChanged("Channels")
End Sub
Private Sub AddChannels()
For i As Integer = 1 To DeviceConfig.Channels.Count
Channels.Add(New ChannelViewModel(i))
Next
End Sub
The Channel UserControl is (simplified):
<UserControl.DataContext>
<ctrls:ChannelViewModel />
</UserControl.DataContext>
<StackPanel Orientation="Horizontal">
<Label x:Name="lblChannelNo" Content="{Binding ChannelNo}" />
<Label x:Name="lblChannelName" Content="{Binding ChannelName}" />
</StackPanel>
And Channel ViewModel (simplified):
Public Class ChannelViewModel
Inherits ViewModelBase
Private aChannelNo As Integer = 0
Property ChannelNo As Integer
Get
Return aChannelNo
End Get
Set(value As Integer)
aChannelNo = value
OnPropertyChanged("ChannelNo")
End Set
End Property
Private aChannelName As String = "N/A"
Property ChannelName As String
Get
Return aChannelName
End Get
Set(value As String)
aChannelName = value
OnPropertyChanged("ChannelName")
End Set
End Property
Okay, the problem was in the Channel View. I think setting the DataContext was overwriting the context from the "A" View. So removing these lines solved the issue:
<UserControl.DataContext>
<ctrls:ChannelViewModel />
</UserControl.DataContext>
I also had to change the binding to :
<DataTemplate>
<ctrls:Channel DataContext="{Binding}" />
</DataTemplate>

WPF Multicolumn Combobox - Binding value is whole POCO instead of a single POCO property

I built a multicolumn combobox in WPF following this SO post: WPF ComboBox Multiple Columns
(Please excuse the mass of code samples ;))
The Combobox XAML
<ComboBox Name="cmbProductTypeMulti" IsEditable="False" Margin="0,2,10,2" MaxDropDownHeight="250"
Text="{Binding Path=AcctData.ProductType}" ItemsSource="{Binding Path=ProductTypeSelection}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Margin="2" Text="{Binding Path=ProductType}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem" BasedOn="{StaticResource ComboBoxItemStyle}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Name="ComboBoxItemBorder" BorderThickness="1">
<Grid Name="ComboBoxItemGrid" TextElement.Foreground="Black">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="150"/>
</Grid.ColumnDefinitions>
<TextBlock Margin="5,3" Grid.Column="0" Text="{Binding ProductType}"/>
<TextBlock Margin="5,3" Grid.Column="1" Text="{Binding Description}" FontSize="10"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<!--- snip --->
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
ViewModel snippet
Public Class AccountsViewModel
Inherits ViewModelBase
//The view model's main Poco
Dim _AcctData As Models.Account
//The multicolumn combobox's Poco
Dim _ProductTypeSelection As IEnumerable(Of Models.ProductType)
Public Property AcctData As Account
Get
Return _AcctData
End Get
Set(value As Account)
MyBase.Set(Of Account)(Function() AcctData, _AcctData, value)
End Set
End Property
Public Property ProductTypeSelection As IEnumerable(Of ProductType)
Get
Return _ProductTypeSelection
End Get
Set(value As IEnumerable(Of ProductType))
MyBase.Set(Of IEnumerable(Of ProductType))(Function() ProductTypeSelection, _ProductTypeSelection, value)
End Set
End Property
...
End Class
View's Poco snippet
Public Class Account
Inherits ObservableObject
Private _ProductType As String
Public Property ProductType As String
Get
Return _ProductType
End Get
Set(value As String)
MyBase.Set(Of String)(Function() ProductType, _ProductType, value)
End Set
End Property
...
End Class
Combobox's Poco
Public Class ProductType
Inherits ObservableObject
Private _ProductType As String
Private _Description As String
Public Property ProductType As String
...
End Property
Public Property Description As String
...
End Property
...
End Class
The Problem
First off, the when I load an account into the view model's AcctData.ProductType, the value is not displayed in the multicolumn combobox. I have a regular combobox bound to the same value which displays AcctData.ProductType initial value properly.
Secondly, when I select an item from the multicolumn combobox, the regular combobox loses its selected item and goes blank. When I go into debug and look at the vm's AcctData.ProductType, I find that it has been assigned the ToString value of the ProductType poco.
So it looks like the multicolumn combobox is trying to use the whole Poco to bind with. How can I get it use a property from a Poco as its binding value?
Thanks
The first problem would probably be solved if you would call the ProductTypeSelection setter from the viewmodel's constructor to initialize the data, because currently it doesn't get set en thus won't raise the propertychanged event, currently your binding only knows about the initial data which is the default null
the second problem is probably caused because the xaml is taken the datatemplate over the controltemplate you have defined and would probably be solved if you put the xaml thats inside the controltemplate inside of the datatemplate
I managed to find a hacky work around.
Since the multicolumn combobox is binding using the Poco's ToString() value, I simply modified ToString() to return the ProductType property, which is the property that I want to be bound! Now the multicolumn combobox behaves as it should - even though its technically binding to the wrong property.

WPF User Control Data Binding Not Working

I'm creating a simple User Control combining a popup with a text view, nothing crazy. When I set it up in a window at first to style it all out, it worked perfectly, but when I moved it into a User Control to actually finish it up, it won't work correctly any more.
I pass a min and max value into the control and then it automatically creates a list of numbers to pick from in that range. In the User Control, the list of numbers doesn't get bound correctly, who knows why. Maybe someone can take a look at my code.
I've read a bunch of other questions about this, but don't really know what's happening. I'm not getting any errors in my output window, so no clues there. Anyway, here's the code -
UserControl.xaml
<UserControl x:Class="UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="Me"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<StackPanel>
<TextBox Name="myTextbox"
Height="30"
Margin="0"
FontSize="14"
IsReadOnly="True"
Padding="5,2"
Text="{Binding Value}" />
<Popup x:Name="myPopup"
PlacementTarget="{Binding ElementName=myTextbox}"
StaysOpen="True">
<Popup.Style>
<Style TargetType="{x:Type Popup}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=myTextbox, Path=IsFocused}" Value="True">
<Setter Property="IsOpen" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</Popup.Style>
<StackPanel>
<ListView Name="myListView"
Height="100"
MaxHeight="300"
ItemsSource="{Binding List,
UpdateSourceTrigger=PropertyChanged}"
SelectionChanged="ListView_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<Label Width="100"
Height="30"
Margin="0"
Content="{Binding}"
FontFamily="Segoe UI"
FontSize="14"
Padding="5,2" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Width="200" Height="30" />
</StackPanel>
</Popup>
</StackPanel>
UserControl.xaml.vb
Imports System.ComponentModel
Imports System.Linq.Expressions
Imports System.Collections.ObjectModel
Class UserControl1
' Dependency Properties
Public Shared ReadOnly ListProperty As DependencyProperty = DependencyProperty.Register("List", GetType(ObservableCollection(Of Integer)), GetType(MainWindow))
Public Shared ReadOnly ValueProperty As DependencyProperty = DependencyProperty.Register("Value", GetType(Integer), GetType(MainWindow))
Public Shared ReadOnly MaxValueProperty As DependencyProperty = DependencyProperty.Register("MaxValue", GetType(Integer), GetType(MainWindow))
Public Shared ReadOnly MinValueProperty As DependencyProperty = DependencyProperty.Register("MinValue", GetType(Integer), GetType(MainWindow))
' Properties
Public Property List As ObservableCollection(Of Integer)
Get
Return DirectCast(GetValue(ListProperty), ObservableCollection(Of Integer))
End Get
Set(value As ObservableCollection(Of Integer))
SetValue(ListProperty, value)
End Set
End Property
Public Property Value As Integer
Get
Return DirectCast(GetValue(ValueProperty), Integer)
End Get
Set(value As Integer)
SetValue(ValueProperty, value)
End Set
End Property
Public Property MaxValue As Integer
Get
Return DirectCast(GetValue(MaxValueProperty), Integer)
End Get
Set(value As Integer)
SetValue(MaxValueProperty, value)
End Set
End Property
Public Property MinValue As Integer
Get
Return DirectCast(GetValue(MinValueProperty), Integer)
End Get
Set(value As Integer)
SetValue(MinValueProperty, value)
End Set
End Property
Private Sub ListView_SelectionChanged(sender As System.Object, e As System.Windows.Controls.SelectionChangedEventArgs)
Value = List(myListView.SelectedIndex)
End Sub
Private Sub UserControl1_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles Me.Loaded
List = New ObservableCollection(Of Integer)
' Add all available numbers into the list
For iCounter As Integer = MinValue To MaxValue
List.Add(iCounter)
Next
' Set the selected index on the list for the value
myListView.SelectedIndex = Value - MinValue
End Sub
End Class
Just for reference, when I tested this out, I set the Min and Max values myself in both the window and usercontrol setup.
I think that you have made the same mistake that I used to when I first started learning WPF. I can't guarantee that this is the cause of your problem, but as it's the only one that I can see, I'll address it. Unfortunately, there are so many poor tutorials and quick examples that show the connecting of a UserControl.DataContext to itself:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Or:
DataContext = this;
Now this is perfectly acceptable if you don't want to Bind to the UserControl externally because it's a quick and easy way to connect with properties defined in the code behind. However, when you want to Bind to the properties from outside the control, you'll find problems. In these instances (if not on all occasions), you should use a RelativeSource Binding to Bind to your code behind properties:
<TextBox Name="myTextbox" Height="30" Margin="0" FontSize="14" IsReadOnly="True"
Padding="5,2" Text="{Binding Value, RelativeSource={RelativeSource AncestorType={
x:Type UserControl1}}}" />
In this Binding, UserControl1 is the name of the UserControl that declared the properties. This should be done on all of the internal Bindings. This way, the DataContext is not set to the UserControl, but the Bindings still find the properties.
You can find out more about RelativeSource from the RelativeSource MarkupExtension page on MSDN.
As I cant provide a comment to Sheridan good answer I have to provide a new answer, sorry for this.
While I love this solution
DataContext="{Binding RelativeSource={RelativeSource Self}}"
it fails (as Sheridan pointed out already) fast.
What you can do is just set the DataContext of the content of your User Control
<UserControl x:Class="Example.View.Controls.MyUserControl"
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:controls="clr-namespace:Example.View.Controls"
mc:Ignorable="d">
<Grid DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type controls:MyUserControl}}}">
</Grid>
In this way all following Bindings have less Boilerplate code as you can just bind directly to your DP from the code-behind like:
<Label Content="{Binding MyLabel}"/>
As for Windows Apps (Windows 8 and Windows 10 UWP) the way to go is to give your control a name and reference it within your XAML file using Path and ElementName:
<UserControl
x:Class="MyControl"
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"
x:Name="Control"
mc:Ignorable="d" >
<Grid Height="240" VerticalAlignment="Top">
<Rectangle Fill="{Binding ElementName=Control, Path=Background}" />
</Grid>
</UserControl>
``

Resources