WPF - Binding between usercontrol - wpf

I have a user control (say A) with two ContentPresenter each binded to another usercontrol (say B and C).
In one of the two UserControl(say B) I have a listBox of MyItem.
<ListBox ItemsSource="{Binding MyItemList}" SelectedItem="{Binding SelectedMyItem}">
MyItem code:
Public Class MyItem
Implements INotifyPropertyChanged
Private m_Name As Integer
Public Property Name As Integer
Get
Return m_Name
End Get
Set(value As Integer)
m_Name = value
End Set
End Property
...
End Class
In the other one(say C) I have TextBoxes that I want to bind to SelectedItem properties(say MyItem.Name).
<TextBox Text="{Binding SelectedItem.Name}"/>
Is it possible in some way? Because I can't find one.

What you're saying is close to impossible.
Bind the textbox text to some property in view model c. Bind the list selected item to some property in view model b.
Emit an event with argument as the Name value as string when selected item is changed. Handle event in viewmodel c and update the textbox text with the received Name argument.
This would do the trick.
Another possible way is to create b and c view model instances in A viewmodel and update cviewmodelinstance.TextboxPropertyName when bviewmodelinstance.selectedListItem property is changed.

Related

Adding Items to a DataGrid (MVVM)

Goal
To add a list of a custom class object (DamagedItems) to a DataGrid using the Model, View, ViewModel (MVVM) way of doing things.
I want the user to be able to create entries of damaged parts (deemed improper during inspection of a machine).
What I have done
I have created:
A window: wDamagedItems.xaml in which it's DataContext is set to DamagedItemViewModel
A Model: DamagedItemModel.vb which implements INotifyPropertyChanged
A ViewModel: DamagedItemViewModel.vb where I set properties of classes such as my DamagedItemModel
An ObservableCollection: DamagedItemList.vb which inherits an ObservableCollection(Of DamagedItemModel)
Since my DataContext is set to the DamagedItemViewModel, here is how I setup the properties:
Public Class DamagedItemViewModel
Private _DamagedItem As DamagedItemModel
Private _Add As ICommand
Private _DamagedItems As DamagedItemList
Public Property DamagedItem As DamagedItemModel
Get
Return _DamagedItem
End Get
Set(value As DamagedItemModel)
_DamagedItem = value
End Set
End Property
Public Property DamagedItems As DamagedItemList
Get
Return _DamagedItems
End Get
Set(value As DamagedItemList)
_DamagedItems = value
End Set
End Property
Public Property Add As ICommand
Get
Return _Add
End Get
Set(value As ICommand)
_Add = value
End Set
End Property
Public Sub New()
DamagedItem = New DamagedItemModel("", "", "")
DamagedItems = New DamagedItemList
Add = New DamagedItemAddEntryCommand(Me)
End Sub
Public Function CanUpdate() As Boolean
If DamagedItem.Description = "" Then Return False
If DamagedItem.Initiales = "" Then Return False
Return True
End Function
Public Sub AddEntry()
DamagedItems.Add(DamagedItem) 'Items get added to the datagrid
DamagedItem = New DamagedItemModel 'Does not seem to clear textboxes
End Sub
End Class
Here is how my XAML is set up:
<DataGrid ItemsSource="{Binding Path=DamagedItems}" AutoGenerateColumns="True" HorizontalAlignment="Stretch" Margin="12,90,12,0" Name="DataGrid1" VerticalAlignment="Top" Height="229" / >
<TextBox Text="{Binding DamagedItem.Description, UpdateSourceTrigger=PropertyChanged}" Height="23" HorizontalAlignment="Left" Margin="88,24,0,0" VerticalAlignment="Top" Width="249" />
<TextBox Text="{Binding DamagedItem.Initiales, UpdateSourceTrigger=PropertyChanged}" Height="23" HorizontalAlignment="Left" Margin="88,58,0,0" VerticalAlignment="Top" Width="249" />
As you can see, my textboxes are bound to my Model (which is contained in my ViewModel, which is bound to that Window's DataContext). Whenever I click on my "Add" button, whatever is in the textbox gets added to the DataGrid, but the content in the text boxes stay there.
This step is fine, I write in what I want to add and click on "Add"
After clicking on "Add" i get the following results in the DataGrid, which is fine. The issue is my text boxes are still filled with data yet the Model was cleared (see code after DamagedItemViewModel AddEntry method).
Now when I try to add the following text:
Description: "Part is bent"
Initiales: "A.C"
I get the following result:
The first letter typed in the description gets inputted in the first entry of the DataGrid, then it erases the text in the description textbox. Only then can I keep typing what I want. The same thing occurs for the initiales text box.
Any ideas? If you wish to see more of my code, suggest which portion I should add.
Thank you in advance!
Yup, I remember running into this one. You have to implement iNotifyPropertyCHnaged. This is how the viewmodel class "notifies" the user interface that there has been a change to the underlying property of a binding:
look here:
http://msdn.microsoft.com/en-us/library/ms743695.aspx
You will have to implement this for every property you want reflected back to the view. SO what I do is have a base viewmodel class (ViewModelBase which exposes method RasiePropertyChanged) which implements iNotifyPropertyChanged and then my viewmodles inherit from it. Then I notify the property changed in the property set of the property:
ie:
Public Property Selection As job
Get
Return Me._Selection
End Get
Set(ByVal value As job)
If _Selection Is value Then
Return
End If
_PreviousJob = _Selection
_Selection = value
RaisePropertyChanged(SelectionPropertyName)
End Set
End Property
This seems frustrating at first but is needed to keep the decoupling that MVVM supports. Its easy to implement.

ListBox ItemTemplateSelector doesn't work

I am attempting to use an ItemTemplateSelector on a WPF ListBox and have looked at several examples online. Seemed simple enough but I cannot get it to work. I am hoping that someone can tell me where I've gone wrong:
Fist, I have an DataTemplateSelector class defined as follows:
Public Class DocketDataTemplateSelector
Inherits DataTemplateSelector
Public Overrides Function SelectTemplate(ByVal item As Object, ByVal container As DependencyObject) As DataTemplate
Return DataDocketHeaderTemplate
End Function
Private _DataDocketHeaderTemplate As DataTemplate
Public Property DataDocketHeaderTemplate() As DataTemplate
Get
Return _DataDocketHeaderTemplate
End Get
Set(ByVal value As DataTemplate)
_DataDocketHeaderTemplate = value
End Set
End Property
Private _DataDocketDataTemplate As DataTemplate
Public Property DataDocketDataTemplate() As DataTemplate
Get
Return _DataDocketDataTemplate
End Get
Set(ByVal value As DataTemplate)
_DataDocketDataTemplate = value
End Set
End Property
End Class
Very simple - just returns the DataDocketHeaderTemplate datatemplate for the time being until I can get it to work.
I then have my user control with the following as its resource definition:
<UserControl.Resources>
<DataTemplate x:Key="docketHeaderTemplate">
<TextBlock Text="Header Row Test" Background="Yellow"/>
</DataTemplate>
<DataTemplate x:Key="docketDataTemplate">
<TextBlock Text="Data Row Test" Background="Green"/>
</DataTemplate>
<local:DocketDataTemplateSelector DataDocketHeaderTemplate="{StaticResource docketHeaderTemplate}" DataDocketDataTemplate="{StaticResource docketDataTemplate}" x:Key="myDataTemplateSelector"/>
</UserControl.Resources>
The ListBox in the user control is simply defined like this:
<ListBox ItemsSource="{Binding TestData}" ItemTemplateSelector="{StaticResource myDataTemplateSelector}"/>
Then finally, my TestData list is defined in my bound viewmodel like so:
Private _listTestData As ObservableCollection(Of String) = Nothing
Public Property TestData As ObservableCollection(Of String)
Get
If _listTestData Is Nothing Then
_listTestData = New ObservableCollection(Of String)
_listTestData.Add("Row 1")
_listTestData.Add("Row 2")
_listTestData.Add("Row 3")
End If
Return _listTestData
End Get
Set(ByVal value As ObservableCollection(Of String))
_listTestData = value
NotifyPropertyChanged("TestData")
End Set
End Property
Now, I expect I would see a list of 3 rows in my listbox all saying 'Header Row Test' (since my datatemplateselector is always returning DataDocketHeaderTemplate). But instead I see my core data of
Row 1
Row 2
Row 3
This seems to indicate that my overriding datatemplateselector is not being hit (indeed if I set a breakpoint in DocketDataTemplateSelector, at no time do I see it being hit). Where am I going wrong with this?
Thanks
Sorry i can not post this as comment, i haven't got enough score.
I just tried your example code (my first VB project) and guess what, it works as expected: three times "Header Row Test" on yellow background. I've put the ListBox in a Grid in the UserControl, then put the UserControl in a Grid in a Window, then set the DataContext of the UserControl to a ViewModel object with your TestData property.
Something must be wrong that is not demonstrated by your example code, maybe you can provide more info.

Binding from multiple sources

My scenario:
I've got a Silverlight Application with a View, where i want to bind the textboxes to an object (two-way) and all labels to a dictionary holding the label translations.
My approach was to set the datacontext of the page to a dictionary with two items, one of them is the object and the other is the translation-dictionary.
In xaml the code looks like the following:
<TextBlock Text="{Binding [dict].[name],FallbackValue='Fallback'}" />
<TextBox Text="{Binding [obj].name,Mode=TwoWay}" />
This works initially, if I however change the object on the datacontext, the xaml is not notified about any changes and doesn't update.
I've had a working solution using a Converter for the translations, however due to the limitations on one converterparameter I didn't like the solution. In addition it wasn't possible to place a fallback-value in the textblock, which resulted in "invisible" textblocks while designing the page.
Any suggestions on how to solve this issue? It doesn't have to be using my dictionary, it would be also okay if i could set the datacontext to the object (which works) and bind the labels somehow different.
I know this will get a lot of traditional answers, but I would also like to put forward something completely original we tried (and succeeded) doing ourselves for more efficient localisation of Silverlight using Attached Properties instead of binding:
Localisation of Silverlight projects after completion
What would be the most flexible is rather than setting the DataContext for the view to a dictionary, you would be better off having the DataContext be something like a ViewModel. That is, a simple class that holds multiple properties: one for your "object" and one for your translation dictionary.
Then have the class that acts as your ViewModel implement INotifyPropertyChanged.
Create a method in your class called OnPropertyChanged that takes in a string representing your property name. In that method raise the PropertyChanged event passing in the instance of the ViewModel class and a new PropertyChangedEventArgs passing in the property name.
Back in the properties you created (object and dictionary) in the Set, after setting the value, call OnPropertyChanged passing in the string name of that property. This will notify the UI that the value of this property has changed and will essentially rebind the control to that property.
Finally, bind the Text properties of your controls on your View to the new properties you just created in your ViewModel. That should ensure that the controls on the view stay up to date.
I found a solution, but wasn't able to answer my own question (8h limit..)
I think this is just the approach Hydroslide suggested.
Create a class which holds all data and implements INotifyPropertyChanged
Public Class MyDatacontext
Implements ComponentModel.INotifyPropertyChanged
'Properties
Private _obj As Object
Private _dict As Dictionary(Of String, String)
'Events
Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
'Methods
Public Property Obj As Object
Get
Return _obj
End Get
Set(ByVal value As Object)
_obj = value
'Notify the xaml about the changed Object
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("Obj"))
End Set
End Property
Public Property Dict As Dictionary(Of String, String)
Get
Return _dict
End Get
Set(ByVal value As Dictionary(Of String, String))
_dict = value
'Notify the xaml about the changed translation
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("Dict"))
End Set
End Property
End Class
Define a private var in your page code
Private mycontext As New MyDatacontext
In the constructor of your page, fill your "mycontext" with the desired data
mycontext.Dict = LoadDictionary()
mycontext.Obj = LoadObject()
Me.DataContext = mycontext
Change your xaml to the following
<TextBlock Text="{Binding Dict.[name],FallbackValue=MyFallback}" />
<TextBox Text="{Binding Obj.name,Mode=TwoWay}" />
Update your object/dictionary as you like using
mycontext.Obj = LoadNextObject()

how to get the selected object in a listbox ---wpf

i have three listboxes, whose itemsSource is binded to a list, list and dictionary. i want to add the selected user and selected book to the dictionary with a command but i can't take the selected items. i am trying to obey the mvvm. i have a booklist and a userlist in my viewmodel which are binded to the given listboxes in my view. i couldn't send the selected items to my viewmodel. how can i do this?
thanks for helping in advance.
Just bind a property to SelectedItem:
<ListBox
ItemsSource="{Binding Books}"
SelectedItem="{Binding SelectedBook}"/>
And in the ViewModel
public class Library : INotifyPropertyChanged
{
public ObservableCollection<Book> Books {get;private set;}
public Book SelectedBook {get;set;}
}
In your viewModel, couldn't you create a SelectedBook and a SelectedUser and bind those to the SelectedItem of your ListBoxes? Then, when they change, add them to your dictionary.
You can also use standard binding syntax using a slash (/).
{Binding Books/}
will allow you to bind directly to the current item in a collection.

Mainaining radio selection after item source change

Setup:
I have a combo-box, it's itemsource bound to an ObservableCollection<T> of a custom class, one property is a List<myenum>.
I have an itemscontrol which is databound to the combo-box's selected item List<myenum> property.
The itemscontrol datatemplate creates a list of radiobuttons, each representing the individual enum values in the list.
The Desire:
When I change the value in the combo-box the itemscontrol source is updated. What I want to occur, is if a radio button in the new itemscontrol source is the same as the selected radiobuton in the previous list (before it was updated), this to be checked.
Current Idea:
Asign a Checked event to the radio buttons, which maintains a myenum property in the window class which can be compared against. Make the IsChecked property of the radiobox bind to a converter and compare against the myenum property. To achieve this, I have made the window class extend from IValueConverter, this way the converter function has access to the myenum property.
Issue:
I don't know how to get the IsChecked binding to use the window as the converter. I have tried using relative source in the converter part of the binding, but that doesn't work
IsChecked="{Binding Converter={RelativeSource={RelativeSource Self}}}"
Preferred Answers:
Assistance on correcting the binding syntax if it's possible this way.
Ideas of a more appropriate way of achieving what I'd like.
I also do not know how to use the window as a value converter in xaml. Instead create a standalone value converter class with a public property for the enum type. Next, in the constructor of the window, get a reference to the instance of the value converter and store it in a private member.
XAML:
<local:MyValueConverter x:Key="ConvertSomething" />
Code behind:
private MyValueConverter _myValueConverter;
public Window1()
{
InitializeComponent();
_myValueConverter = FindResource("ConvertSomething") as MyValueConverter;
}
private void RadioButton_Checked(object sender, RoutedEventArgs e)
{
// You can access _myValueConverter here and set its public enum property.
}

Resources