One collection, two bindings - wpf

Is it possible to bind one ObservableCollection to two Listbox ItemsSource's in the same view?
public ObservableCollection<CameraListBoxItem> Window1CameraListBoxItems
{
get { return cameraListBoxItems; }
}
<ListBox x:Name="DeviceList" ItemsSource="{Binding Window1CameraListBoxItems}" />
<ListBox x:Name="DeviceList2" ItemsSource="{Binding Window1CameraListBoxItems}" />
It doesn't seem to work and I don't understand why.
UPDATE
<DataTemplate DataType="{x:Type vm:WindowViewModel}">
<vw:WindowView />
</DataTemplate>
Using MVVM the view is binded to the viewmodel.
public WindowViewModel(ObservableCollection<CameraListBoxItem> items)
{
cameraListBoxItems = items;
}

Yes. You can bind as many elements as you wish to the same source property.
I would check the bindings debugging information in the Output Window. This may not be working because both bindings are incorrect, which could happen if the DataContext isn't set properly.

Related

WPF ListView ScrollViewer.VerticalScrollBarVisibility Binding problem

Iam using WPF with MVVM pattern.
I have a ListView and Iam trying to do a binding with the property ScrollViewer.VerticalScrollBarVisibility , because I need to know in the ViewModel, when the scroll of the List View is Visible to do some things later. My problem is that for any reason, when the ScrollViewer.VerticalScrollBarVisibility is Visible (because there are some many items in the list) the property "MyScrollVisibility" is not being updated in the ViewModel.
Here is my code:
xaml
<ListView
ItemsSource="{Binding Items}"
ScrollViewer.VerticalScrollBarVisibility="{Binding
MyScrollVisibility, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding SelectedItem}" >
ViewModel...
public ScrollBarVisibility MyScrollVisibility
{
get
{
return _myScrollVisibility;
}
set
{
_myScrollVisibility = value;
RaisePropertiesChanged("MyScrollVisibility");
}
}
Thanks in advance for any help or idea

Binding ObservableCollection of strings to templated ListBox

I have this collection
ObservableCollection<string> Urls { get; set; }
inside my data context class. I have a binding to it in my list box:
<ListBox ItemsSource="{Binding Urls}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding .}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The data is diplayed in the list box, the two corresponding-not-shown-here buttons with commands Add and Delete work as well, however, the changing the TextBox does not affect the contents of the collection.
I have tried Mode=TwoWay in binding, but I figured that it is turned on already. I have tried some other options like Validate=OnPropertyChange, however, there is still nothing get updated.
How to make the TextBox inside that templated items in ListBox actually update the Urls property of a datacontext class?
You cannot modify strings; use a wrapper class with one string property, then bind the TextBox to said property. That way the strings in the property can be replaced with the edited ones.

Binding TwoWay to SelectedItem: "Wrong way" synchronization on initialization

I am trying to bind a property of my DataContext to the SelectedItem on a ComboBox like this:
<ComboBox x:Name="ElementSelector"
ItemsSource="{Binding Source={StaticResource Elements}}"
DisplayMemberPath="ElementName"
SelectedItem="{Binding ValueElement, Mode=TwoWay}">
where the Elements resource is a CollectionViewSource (don't know, whether this matters).
When everything is initialized, the property ValueElement of the DataContext is set to the first item in the CollectionViewSource. What I want, is to initialize it the other way around: I would like to set SelectedItem of the ComboBox to the value of the property or null if no matching item is contained.
How can this be done?
EDIT - Additional information:
The ComboBox is part of a DataTemplate:
<DataTemplate x:Key="ReferenceTemplate"
DataType="viewModels:ElementMetaReferenceViewModel">
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<ResourceDictionary>
<views:ElementsForReferenceViewSource x:Key="Elements"
Source="{Binding DataContext.CurrentProject.Elements, ElementName=Root}"
ReferenceToFilterFor="{Binding}"/>
</ResourceDictionary>
</StackPanel.Resources>
<TextBlock Text="{Binding PropertyName}"/>
<ComboBox x:Name="ElementSelector"
ItemsSource="{Binding Source={StaticResource Elements}}"
DisplayMemberPath="ElementName"
SelectedItem=""{Binding ValueElement, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
The ElementsForReferenceViewSource simply derives from CollectionViewSource and implements an additional DependencyProperty which is used for filtering.
The DataContext of the items in the CollectionViewSource look like this:
public class ElementMetaReferenceViewModel : ViewModelBase<ElementMetaReference, ElementMetaReferenceContext>
{
...
private ElementMetaViewModel _valueElement;
public ElementMetaViewModel ValueElement
{
get { return _valueElement; }
set
{
if (value == null) return;
_valueElement = value;
Model.TargetElement = value.Model;
}
}
...
}
For people encountering the same issue
The above code works as expected. The solution was getting the stuff behind the scenes right. Make sure, that the instance of the ViewModel which is the value of the property you want to bind to is definitely contained in the CollectionViewSource.
In my case the issue was deserializing an object tree incorrectly, so objects were instantiated twice. Then for each object a distinct ViewModel was initialized and then obviously the value of the property was not contained in the list.
Remark
To check whether this is an issue in your case, you can try the following:
Override the ToString() methods of the ViewModels displayed in the ComboBox like this:
public override string ToString()
{
return "VM"+ Model.GetHashCode().ToString();
}
Then you can easily compare the items in the source collection with the value on your property. Not the most professional way, but it did the job for me.

Binding a ListBox to an ObservableCollection of ViewModels

Is there a convention when using MVVM to bind the items of a ListBox to a ViewModel?
In the below XAML, I'm creating a ListBox of buttons. The ListBox is bound to an observable collection from my ViewModel. I then want to bind the button's Command property to an ICommand. The problem is that when I add that binding, I'm binding against the data object, not the ViewModel.
Do I just change the MyListOfDataObjects property to be a list of ViewModels? If so, where do I instantiate those new objects? I'd prefer to use dependency injection since they will have several dependencies. Do I change the GetData lambda?
In general: what's considered good practice here? I wasn't able to find any examples for this situation, although I assume it is rather common.
I'm using the MVVMLight framework, but I'm willing to look at any other frameworks.
<Window x:Class="KeyMaster.MainWindow"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<Window.Resources>
<ResourceDictionary>
<DataTemplate x:Key="MyDataTemplate">
<Button Command="{Binding ButtonPressedCommand}"
CommandParameter="{Binding .}"
Content="{Binding Name}" />
</DataTemplate>
</ResourceDictionary>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<ListBox ItemsSource="{Binding MyListOfDataObjects}"
ItemTemplate="{StaticResource MyDataTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"
IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ListBox>
</Grid>
</Window>
I'm using the standard MVVMLight ViewModel:
using GalaSoft.MvvmLight;
using KeyMaster.Model;
using System.Collections.ObjectModel;
namespace KeyMaster.ViewModel
{
public class MainViewModel : ViewModelBase
{
private readonly IDataService _dataService;
private ObservableCollection<MyData> _myListOfDataObjects;
public MainViewModel(IDataService dataService)
{
_dataService = dataService;
_dataService.GetData(
(item, error) =>
{
if (error != null)
{
return;
}
MyListOfDataObjects = new ObservableCollection<MyData>(item);
});
}
public ObservableCollection<MyData> MyListOfDataObjects
{
get { return _myListOfDataObjects; }
set
{
if (_myListOfDataObjects == value) return;
_myListOfDataObjects = value;
RaisePropertyChanged(() => MyListOfDataObjects);
}
}
}
}
Thanks.
In MVVM, there is a clear seperation between the raw data (also known as the Model) and the ViewModel. The ViewModel is the one who is in charge of parsing the data and even modifying it to whatever form it wishes, before passing it to the View.
A simple example is having the Model as XML and having the ViewModel parse it, take only a specific property (for example a "Name") from each element and add them to a list. Only this list will be shown in the View.
That said, I guess you can see where I'm going - the Command should be in the ViewModel not in the Model. As you stated by yourself, you should keep as much of the UI logic out of both the VM and the Model.
If you have a specific command that does something specific on a certain type of data, you can want it in a more "general" type of ViewModel, you can use the CanExectue to only allow this command in specific cases. But still, the command should sit in the ViewModel.
In your specific case, I don't see a problem having the command in the ViewModel, and when raised it will do whatever you need on your data. You don't need a list of ViewModels, you need only one.
I would say it'd depend where you want the functionality of the button-press. If it is always related to the MyData object then (if possible) would it be so out of place to put the Command in the MyData object? (ps. I wouldn't call your MyData object ViewModels just because you're adding a command property to them, as they're not associated with a view)
Alternatively if you want the command in the VM then you could try bind the command using the datacontext of the window. ie something like;
<Button Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.ButtonPressedCommand}"
CommandParameter="{Binding .}"
Content="{Binding Name}" />
Although I've had trouble in the past with that and went with adding the command to the individual objects.

MVVM ListBox controlling a Content Control

I've been going round in circles with this for a couple of days, and I'm hoping a WPF guru can see where I'm going wrong.
I'm setting CurrentViewModel in code. The Selected item of my ListBox and the Content of my ContentControl bind correctly. But when changing the selected item in the Listbox via the UI the CurrentViewModel is being set but the Content Control is not being updated.
I'm using a data template to map my Views and View Models.
<DataTemplate DataType="{x:Type ViewModel:MyViewModel}">
<View:MyView />
</DataTemplate>
I have a ListBox which is bound to an observable collection of ViewModels. The Selected Item is bound to the current view model.
<ListBox ItemsSource="{Binding MyViewModelCollection}" DisplayMemberPath="DisplayName" SelectedItem="{Binding CurrentViewModel, Mode=TwoWay}"/>
I also have a content control that is also bound to the CurrentView Model
<ContentControl Content="{Binding CurrentViewModel, Mode=TwoWay}"/>
This is the property that they are both bound to
public MyViewModel CurrentViewModel
{
get
{
return _currentViewModel;
}
set
{
if (_currentViewModel== value) return;
_currentViewModel= value;
OnPropertyChanged("CurrentViewModel");
}
}
I've edited the names for clarity and removed formatting information.
Any help greatly appreciated.
Cheers,
Daniel
EDIT: Came across the link How can I debug WPF bindings?. I set a break point on the Content binding and it does indeed only get called once when the binding is first set.
You should not be setting TwoWay as the mode on your ContentControl:
<ContentControl Content="{Binding CurrentViewModel, Mode=OneWay}"/>
This is because you intend your ContentControl to read the value, but never write it.
As an aside, you can also bind the ContentControl to the currently selected item in the collection, rather than to that property by doing this:
<ListBox ItemsSource="{Binding MyViewModelCollection}"
DisplayMemberPath="DisplayName"
IsSynchronizedWithCurrentItem="True"/>
<ContentControl Content="{Binding MyViewModelCollection/}"/>
The "slash" (/) at the end of the collection indicates the current item selected in the collection and setting that current item property is as simple as setting the IsSynchronizedWithCurrentItem equal to true.
A lot of times I find with this combination, I really don't need the extra property on my view model.
Anyway, I hope this helps.

Resources