WPF share common property between controls - c#

i've two content controls how can i share a common property between them,
for example if i select some value from combobox in the first content control,
how can the second control know it
<telerikNavigation:RadTabItem Header="1">
<StackPanel>
<ContentControl Content="{Binding EGRPExtractViewModel.View}" />
</StackPanel>
</telerikNavigation:RadTabItem>
<telerikNavigation:RadTabItem Header="2">
<ContentControl Content="{Binding EGRPRightObjectViewModel.View}" />
</telerikNavigation:RadTabItem>
Thanks

You need to use two way binding then respond to the property changing in your ViewModel.
<ContentControl Content="{Binding EGRPRightObjectViewModel.View,Mode=TwoWay}" />
See the MSDN docs for how to respond to changed properties: http://msdn.microsoft.com/en-us/library/ms743695(v=vs.110).aspx

You do not bind view properties. You can bind control properties in same view, so one possibility for you will be to create a control which exposes bindable properties specifically for this reason.
When using mvvm normally view-model should provide all needed properties to the view. If it's a property from another view-model, then it is still have to be provided by view-model of this view (search for questions about how to pass data between view-models to example, here is one).

Related

WPF and MVVM: bind UserControl to XAML dynamically

seems like a trivial task: i am building a wpf application, using MVVM pattern. what i want is dynamically change part of a view, using different UserControls, dependent on user input.
let's say, i have got 2 UserControls, one with a button, and another with a label.
in main view i have a container for that. following XAML "works":
<GroupBox Header="container" >
<local:UserControlButton />
</GroupBox>
and a UserControl element with buttons pops up. if i change it to another one, it works too.
question is how to feed that groupbox dynamically. if i put something like that in my model view:
private UserControl _myControl;
public UserControl MyControl
{
get
{
return _myControl;
}
set
{
_myControl= value;
InvokePropertyChanged("MyControl");
}
}
and change my view XAML to something like:
<GroupBox Header="container" >
<ItemsControl ItemsSource="{Binding MyControl}" />
</GroupBox>
and feed it from command with usercontrol for button or for label: nothing happens, although "MyControl" variable is set and is "invoke property changed"..
Obviously there are many ways to skin this particular cat - but to answer the question of why it doesn't work you need to look into the ItemsSource property of ItemsControl on MSDN.
The items control is designed to show multiple items, provided through an IEnumerable passed to the ItemsSource property. You are passing a UserControl, so the binding will fail.
For your example, I would change the ItemsControl to a ContentControl and bind the content to your MyControl property. This should then work.
<GroupBox Header="container" >
<ContentControl Content="{Binding MyControl}" />
</GroupBox>
However, I would strongly recommend looking into other ways of doing this - having a control in your VM breaks MVVM to my mind. Depending on what you are doing look at data templates - #Sheridan's link in the comments provides an great description of a way to do it.
Couldn't post this as a comment so adding as answer..
Have a look at this:
Implementing an own "Factory" for reusing Views in WPF
It uses DataTemplates but doesn't require the DataTemplate section for each view. If you potentially have a lot of user controls/views you wish to display or you are reusing through multiple views or you are intending to actually dynamically generate a view (versus just loading an existing user control) then this might suite your needs.

Bind multiple views to multiple viewmodels

I have a WPF window displaying different self-defined Views. So far I was able to use everything I learned about MVVM :)
Now I got to a new "problem": I have 10 entities of the same view in a bigger view. These ten view-entities contain a set of controls (textbox, combobox etc.) but are all consistent.
So how do I bind these Views to a ViewModel?
I thought about having 10 instances of the ViewModel in the "higher-level" ViewModel and give the views fix-defined the instances of the VM as datacontext.
My question is now --> Is there a easier (or more convienient) way to bind many (identical) views to their viewmodels?
Code-Example:
View Model:
private PanelViewModel _panelViewModel1 = new PanelViewModel();
public PanelViewModel PanelVM1
{
get { return _panelViewModel1; }
}
View-Example:
<myControls:vwPanel HorizontalAlignment="Left" x:Name="vwPanel1"
VerticalAlignment="Top" DataContext="{Binding Path=PanelVM1}"/>
What bothers me is that I would need this logic ten times for ten views?
UPDATE:
To answer some questions: I want to show one view 10 times (in my example) I defined my own view by inheriting from UserControl. So my vwPanel inherits from UserControl. The 10 vwPanels are just placed inside a StackPanel inside a Grid.
It's not about displaying data, as you pointed out, there would be a listview or a datagrid a better place to start. It's a special case where I need this much input-controls :/
UPDATE2: What I hoped for was more like defining a List of ViewModels and Bind my 10 Views to one of this List. But this will not work will it? At least I wouldn't know how to refernce one "special" entitiy in the list out of XAML...
Typically I use implicit DataTemplates for mapping Views to ViewModels. They can go in <Application.Resources>, <Window.Resources> or even in under specific elements only such as <TabControl.Resources>
<DataTemplate DataType="{x:Type local:PanelViewModel}">
<myControls:vwPanel />
</DataTemplate>
This means that anytime WPF encounters an object in the VisualTree of type PanelViewModel, it will draw it using vwPanel
Objects typically get placed in the VisualTree through an ItemsSource property
<ItemsControl ItemsSource="{Binding CollectionOfAllPanels}" />
or by using a ContentControl
<ContentControl Content="{Binding PanelVM1}" />
If I understand your question correctly, you have a collection of something that you what to represent visually. That is, you have several viewmodels that you want to define a single view for, but show X number of times. Your example shows you using a panel as your view for the "PanelViewModel"...what is the parent item's control for the vwPanel? Assuming you're using something like a ListBox, you can define a custom DataTemplate that contains your vwPanel and assign that DataTemplate to your ListBox.ItemTemplate.
For example:
<Window.Resources>
<DataTemplate x:Key="myVMTemplate" TargetType="{x:Type myViewModels:PanelViewModel}">
<myControls:vwPanel HorizontalAlignment="Left" VerticalAlignment="Top"/>
</DataTemplate>
</Window.Resources>
<ListBox ItemsSource="{Binding Path=MyCollectionOfPanelVMs}"
ItemTemplate="{StaticResource myVMTemplate}" />
I haven't verified that this works.

How do I set view/view model data template at runtime?

This MVVM stuff is making my head hurt. I have an application which has a list of editors in a left pane. On the right is a tab control where the editors will be displayed. I have a main application view model that contains a collection of view models. I call this collection Workspaces. This is borrowed from the MvvmDemoApp that Microsoft provides here.
public ObservableCollection<WorkspaceViewModel> Workspaces
{
get
{
...
}
}
These workspaces are bound to a tab control in the main application window like so:
<DataTemplate x:Key "WorkspacesTemplate">
<TabControl
IsSynchonizedWithCurrentItem="True"
ItemSource="{Binding Workspaces}"
SelectedItem="{Binding ActiveWorkspace}"/>
</DataTemplate>
...
<ContentControl
Content="{Binding}"
ContentTemplate="{StaticResource WorkspacesTemplate}"/>
The view models are tied to a view using DataTemplates like so:
<DataTemplate DataType="{x:Type vm:MessageLogViewModel}">
<vw:MessageLogView/>
</DataTemplate>
This works fine. However, now I need to make the application configurable where the list of editors are read from a config file. I imagine this config file will contain the view and view model components for each editor. But, how do I tie the two together so that when someone binds to a view model (or a collection of view models), the correct view gets displayed (similar to what the DataTemplate does but in code, not XAML)?
I'm trying to stay away for Inversion of Control (IoC) techniques. I'm not sure our team is ready for that must sophistication.
IoC is the perfect solution for this however without this option you could creating the XAML data template in the view model using an XmlWriter and expose it as a property to bind to.
Edit: Bindings
You have your list of view models. Create and expose this XamlTemplate property in each view model (in a base view model class). The property should create Xaml along the lines of:
<DataTemplate xmlns:vw="...">
<vw:MessageLogView/>
</DataTemplate>
Then use a ContentControl to bind to:
<ContentControl Content="{Binding ViewModel}"
ContentTemplate="{Binding ViewModel.XamlTemplate}" />

WPF implicit datatemplate with observablecollection

I'm new to WPF and using MVVM. I have a view in which I want to display different content according to what a user selects on a menu. One of those things is another user control Temp which has a view model (TempVM) so I am doing this:
<ContentControl Content="{Binding Path=TempVM}"/>
and TempVM (of type TempViewModel)is null until the user clicks a button. Its data template is this
<DataTemplate DataType="{x:Type vm:TempViewModel}">
<view:Temp />
</DataTemplate>
That's fine, but the other thing I want to do is show a listbox when a user clicks a different menu item. So I am trying to do
<ContentControl Content="{Binding Path=Missions}"/>
(Missions is an observable collection of MissionData) and trying to template it like this:
<DataTemplate DataType="{x:Type ObservableCollection(MissionData)}">
<StackPanel>
<ListBox ItemsSource="{Binding}" SelectedItem="{Binding Path=MissionData, Mode=TwoWay}" DisplayMemberPath="MissionName" SelectedValuePath="MissionId" />
<Button Content="Go"/>
</StackPanel>
</DataTemplate>
But the compiler doesn't like the type reference. If I try doing it by giving the template a key and specifying that key in the ContentControl it works but obviously I see the ListBox and button when there's no Missions. Obviously I could make a user control and viewmodel and follow the same pattern as I did for the TempVM but it seems over the top. Am I going the right way about this and what do I need to do?
From what i see is that you try to use a Collection as a dataobject which is in my opinion bad practice. Having a DataTemplate for a collection is also problematic, like you already have witnessed. I would advice you to use a ViewModel for your missions collection.
class MissionsSelectionViewModel
{
public ObservableCollection<Mission> Misssions;
public MissionData SelectedMission;
public ICommand MissionSelected;
}
and modify your datatemplate to
<DataTemplate DataType="{x:Type MissionsSelectionViewModel}">
<StackPanel>
<ListBox ItemsSource="{Binding Missions}" SelectedItem="{Binding Path=MissionData, Mode=TwoWay}" DisplayMemberPath="MissionName" SelectedValuePath="MissionId" />
<Button Content="Go" Command="{Binding MissionSelected}/>
</StackPanel>
</DataTemplate>
If I were to follow your pattern of implicit templates, I would derive a custom non-generic collection MissionDataCollection from ObservableCollection<MissionData> and use it to keep MissionData items. Then I would simply reference that collection in DataType. This solution gives other advantages like events aggregation over the collection that are useful.
However, it seems to me that the best solution is the following.
Add a IsMissionsListVisible property to your VM.
Bind the Visibility property of the ContentControl showing the list to the IsMissionsListVisible property.
Use a keyed DataTemplate resource.
Implement the logic that determines if IsMissionsListVisible. Supposedly it should be true when there is at least one mission in the selected item. But the logic may be more complex.
I would do it this way. In fact, I do it this way usually, and it gives several benefits. The most important is that I can explicitly control the logic of content visibility in various situations (e.g. async content refresh).

How to set focus to textbox using MVVM?

How to focus a textbox from ViewModel wpf?
<TextBox Name="PropertySearch"
Text="{Binding UpdateSourceTrigger=PropertyChanged,
Mode=TwoWay, Path=PropertySearch,
ValidatesOnDataErrors=True}"
Width="110"
Height="25"
Margin="10" />
You can do this by adding a property to your ViewModel (or use an existing property) that indicates when the SetFocus should happen but the View should be responsible for actually setting the focus since that is purely View related.
You can do this with a DataTrigger.
View:
<Grid Name="LayoutRoot" DataContext="{StaticResource MyViewModelInstance}">
<Grid.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding UserShouldEditValueNow}" Value="True">
<Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=PropertySearch}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<TextBox Name="PropertySearch" Text="{Binding UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, Path=PropertySearch, ValidatesOnDataErrors=True}" Width="110" Height="25" Margin="10" />
</Grid>
ViewModel:
// When you think the view should set focus on a control
this.UserShouldEditValueNow = true;
The example above is simplified by just using a boolean ViewModel property "UserShouldEditValueNow". You can add a property like this to your ViewModel or use some other exising property that indicates this state.
Note: So why is it done this way in MVVM? One reason is, suppose the View author decided to replace the TextBox with a ComboBox, or even better, suppose your property was an integer value that had both a TextBox to view/edit the number and a Slider as another way to edit the same value, both controls bound to the same property... how would the ViewModel know which control to set focus on? (when it shouldn't even know what control, or controls, are bound to it in the first place) This way the View can select which control to focus by changing the ElementName binding target in the DataTrigger Setter.
Happy coding!
The question you should be asking yourself is "why does my ViewModel need to know which control has the focus?"
I'd argue for focus being a view-only property; it's an interaction property, and has nothing to do with the conceptual state. This is akin to the background color of a control: why would you represent it in the VM? If you need to manage the focus in a custom way, it's probably better to use a view-level object to do the job.
In your parent control, add the following property:
FocusManager.FocusedElement="{Binding ElementName=PropertySearch}"
While purists may argue for leaving this out of the VM, there are cases where it may make sense to do so from the VM.
My approach has been to make the view implement an interface, pass that interface to the ViewModel, and then let the VM call methods on the interface.
Example:
public interface IFocusContainer
{
void SetFocus(string target);
}
A couple things to keep in mind:
A VM might serve more than one instance of a view, so your VM might want to have a collection of references to IFocusContainer instances, not just one.
Code the VM defensively. You don't know whether there are 0, 1 or 20 views listening.
The "target" parameter of SetFocus() should probably be "loosely" coupled to the VM. You don't want the VM caring about the exact control names in the UI. Rather, the VM should indicate a name that is defined solely for focus management. In my case, I created some attached properties that would allow me to "tag" controls with "focus names".
To implement the interface, you can:
Implement it in the code-behind
Create some behaviors that know how to attach to the ViewModel that is present in the DataContext.
There's nothing wrong with implementing it on the Code Behind, but the behavior approach does allow a XAML only hookup if that's important to you.
In the implementation of the interface, you can use the visual tree to locate the control, or you could just code up a switch statement for a known set of focusable items.

Categories