i have view:
<Grid>
<!--Some Stuff-->
<Control XXX="{Binding ButtomControl}"/>
<!--Some Stuff-->
</Grid>
i have VM:
public sealed class SelectionDialogV3VM : PropertyChanges
{
// Some Stuff
public Control ButtomControl
{
get{return _buttomControl;}
set
{
_buttomControl = value;
OnPropertyChanged("ButtomControl");
}
}
// Some Stuff
}
My objective: in run time change some view (ButtomControl) inside of my main view.
But, i can not do proper binding because i do not know the XXX property.
thanks
Try something like this :
<ContentControl Content="{Binding ButtomControl}"/>
But honestly, having a property in your ViewModel of type Control is not a good omen :D
I just wanted to add something else:
Referencing a UI control in the view model should be avoided at all cost.
If you want to switch the view via the view model, try using DataTemplates and a ContentControl instead.
See:
http://rachel53461.wordpress.com/2011/05/28/switching-between-viewsusercontrols-using-mvvm/
Use a ContentPresenter:
<ContentPresenter Content="{Binding ButtomControl}"/>
Anyway, it's odd to bind to a control!
Thanks you all, all answers was valuable
Finally, i used DataTriggers, as described here:
MVVM : how to switch between views using DataTemplate + Triggers
Thanks
Related
I have a WPF control whose content completely depends on a property of its data context. For the sake of this, let's just say the control's DataContext is of type Product, which has a Status property of InStock, OutOfStock, or Discontinued.
I have individual user controls for each of those status types. I could, and have, created some kind of panel that binds the visiblity of each to Product.Status. But that created problems, since some of the user controls ended up with funky stuff because some depend on various properties being set. And in my actual application, there are many statuses, so the visualtree gets too big for my taste.
I solved the problem by creating and in my code, I check for a status change on the DataContext and set the appropriate child in a big switch statement. I would like to do this in XAML if possible. I want the child to be set on demand, so I assume I'll need to use templates. Something like this:
SwitchControl would derive from Decorator or Border, whatever.
<SwitchControl Property="Status">
<SwitchControl.Possibilities>
<Possibility Value="Discontinued">
<Possibility.Template>
<DiscontinuedView />
</Possibility.Template>
</Possibility>
<Possibility Value="InStock">
<Possibility.Template>
<InStockView />
</Possibility.Template>
</Possibility>
<SwitchControl.Possibilities />
</SwitchControl>
It would be even better if I could shorten the whole thing to:
<SwitchControl>
<Possibility Value="Discontinued">
<DiscontinuedView />
</Possibility>
<Possibility Value="InStock">
<InStockView />
</Possibility>
</SwitchControl>
Point being, only one child would exist at any given time. Anyone know of a way to get this done? I looked around in MVVM frameworks and couldn't find anything. Otherwise I'll experiment with creating a custom control myself.
You might want to take a look at the DataTemplateSelector class. This allows you to define templates based on different criteria, e.g. the type of the current DataContext. An example could look somewhat like the following:
public class MyDataTemplateSelector : DataTemplateSelector
{
public DataTemplate DiscontinuedDataTemplate { get; set; }
public DataTemplate InStockDataTemplate { get; set; }
public DataTemplate OutOfStockDataTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var product = item as Product;
switch (product.Status)
{
case Status.InStock:
return InStockDataTemplate;
case Status.Discontinued:
return DiscontinuedDataTemplate;
case Status.OutOfStock:
return OutOfStockDataTemplate;
}
// Fallback
return DiscontinuedDataTemplate;
}
}
...and use it in the following way:
<Window.Resources>
<DataTemplate x:Key="DiscontinuedDataTemplate">
<DiscontinuedView />
</DataTemplate>
<DataTemplate x:Key="InStockDataTemplate">
<InStockView />
</DataTemplate>
<DataTemplate x:Key="OutOfStockDataTemplate">
<OutOfStockView />
</DataTemplate>
<!-- DataTemplate Selector -->
<local:MyDataTemplateSelector x:Key="MyTemplateSelector"
DiscontinuedDataTemplate="{StaticResource DiscontinuedDataTemplate}"
InStockDataTemplate="{StaticResource InStockDataTemplate}"
OutOfStockDataTemplate="{StaticResource OutOfStockDataTemplate}"/>
</Window.Resources>
<ContentControl ContentTemplateSelector="{StaticResource MyTemplateSelector}" Content="{Binding Product}"/>
Thanks for the suggestion andreask. I ended up creating a control that I think solves the problem more directly. I've been working on a WPF helper library that I'll post to nuget in the future, but if you want to use it now, it's at:
https://gist.github.com/StevePotter/b17f8d4b2657a2d2610390a11fb57e03
Example XAML is included. I hope this is useful for someone!
I have searched and tried for days and finally must ask the question here.
I have a Silverlight 5 application, Using MVVM Light, where I want to be able to dynamically switch views in the main view.
For the sake of simplicity, lets say I have 2 buttons.
Button1 will switch to TestView1.
Button2 will switch to TestView2.
<Button Content="TestView1" Grid.Column="1" Command="{Binding CallTestView1Command}" HorizontalAlignment="Left" Margin="185,17,0,0" VerticalAlignment="Top" Width="75"/>
<Button Content="TestView2" Grid.Column="1" Command="{Binding CallTestView2Command}" HorizontalAlignment="Left" Margin="280,17,0,0" VerticalAlignment="Top" Width="75"/>
The way I have done it is by binding a relaycommand to the button and then instanciating a new viewmodel of the corresponding view.
ie:
private RelayCommand _callTestView1Command;
public RelayCommand CallTestView1Command
{
get
{
return _callTestView1Command ??
(_callTestView1Command = new RelayCommand(() =>
{
CurrentView = ViewModelLocator.NinjectKernel.Get<TestViewModel1>();
}));
}
}
The CurrentViewmodel is then set to the new viewmodel.
In the MainView I have bound the CurrentView to a ContentControl:
<Border x:Name="displayedView" Grid.Row="2">
<ContentControl Content="{Binding CurrentView}" />
</Border>
This will actually work to some extend, since the CurrentView will change but instead of actually showing the content of the view it simply shows the Namespace of the ViewModel that is instanciated.
So far I have primarily used the knowledge taken from these sources:
http://rachel53461.wordpress.com/2011/05/28/switching-between-viewsusercontrols-using-mvvm/
Loading Views into ContentControl and changing their properties by clicking buttons
but they do not solve my problem, or I do not quite understand how to actually show the views.:-(
So does anyone have a good explanation on how to switch the views correct in Silverlight 5 using MVVM Light from GalaSoft.
Thanks
The part you are missing is the DataTemplates that tell WPF how to render your ViewModels
<Window.Resources>
<DataTemplate TargetType="{x:Type local:TestViewModel1}">
<local:TestView1 />
</DataTemplate>
<DataTemplate TargetType="{x:Type local:TestViewModel2}">
<local:TestView2 />
</DataTemplate>
</Window.Resources>
When you insert an object in the Visual Tree, such as placing a ViewModel object in ContentControl.Content, it will get drawn by default using a TextBlock bound to the .ToString() of the object, which is why you are only seeing the namespace.classname of the ViewModel in your ContentControl
By defining an implicit DataTemplate in your Resources somewhere (that's a DataTemplate with only a TargetType defined - no x:Key), you are telling WPF to draw the specified object using the specified DataTemplate anytime it tries to draw that object, instead of using the default TextBlock bound to the .ToString() of the object.
It should be noted that implicit DataTemplates are not supported in earlier versions of Silverlight, however they are supported in 5.0+. For earlier versions of Silverlight, I usually use a DataTemplateSelector instead.
Id first suggest that you do not display your views via a ContentControl but look into using the navigation Frame in the silverlight toolkit. Also, we dont want our ViewModel creating Views... that'd not be so good. We don't mind, however, if our ViewModel does business logic and DETERMINES which view to show. Get the toolkit here: http://silverlight.codeplex.com/
Now setup your XAML as so in your main page:
<Border x:Name="displayedView" Grid.Row="2">
<navigation:Frame x:Name="ContentFrame" />
</Border>
Since you are using MVVM Light, we will use messaging. Your View model will get the command to change views, determine which view to change, then send a message to the main page to instruct it to change views.
Setup a listener in your main page for a navigate request as so:
public MainPage()
{
InitializeComponent();
Messenger.Default.Register<Uri>(this, "NavigationRequest", (uri) => ContentFrame.Navigate(uri));
}
Next, setup your command in your view model.
private RelayCommand _callTestView1Command;
public RelayCommand CallTestView1Command
{
get
{
return _callTestView1Command ??
(_callTestView1Command = new RelayCommand(() =>
{
Messenger.Default.Send<Uri>(new Uri("/Views/.../Page.xaml", UriKind.Relative), "NavigationRequest");
}));
}
}
These are the basics that work for me. You can expand on this and get real "architecty". For example, you can create a base class for you view models that sends the navigation requests, create a helper class that generates URIs (so they are not hard coded everywhere in your app, etc etc. Good luck!
So i actually solved this problem, in a way where there is no need to create datatemplates in the MainView, which i did not like. imo the MainView should know nothing about the views it is displaying, when we are talking about switching the views.
Prerequisite: You must use MVVM Light from GalaSoft for this solution
This is my test solution:
Two buttons are added to my MainView, Each button will open a new view. The clickevent are bound to Commands.
<Button Content="TestView1" Grid.Column="1" Command="{Binding CallTestView1Command}" HorizontalAlignment="Left" Margin="185,17,0,0" VerticalAlignment="Top" Width="75"/>
<Button Content="TestView2" Grid.Column="1" Command="{Binding CallTestView2Command}" HorizontalAlignment="Left" Margin="280,17,0,0" VerticalAlignment="Top" Width="75"/>
In the MainView i have a Border that should contain the views than can switch.
Since all views inherit from UserControl i bind the content to the property CurrentView of the MainViewModel
<Border x:Name="displayedView" Grid.Row="2">
<UserControl Content="{Binding CurrentView}" />
</Border>
In the MainViewModel i have the property CurrentView.
public const string CurrentViewPropertyName = "CurrentView";
private UserControl _currentView;
/// <summary>
/// Sets and gets the "CurrentView property.
/// Changes to that property's value raise the PropertyChanged event.
/// </summary>
public UserControl CurrentView
{
get
{
return _currentView;
}
set
{
if (_currentView == value)
{
return;
}
RaisePropertyChanging(CurrentViewPropertyName);
_currentView = value;
RaisePropertyChanged(CurrentViewPropertyName);
}
}
When a button is clicked the corresponding Command is called in the MainViewModel:
private RelayCommand _callTestView1Command;
public RelayCommand CallTestView1Command
{
get
{
return _callTestView1Command ??
(_callTestView1Command = new RelayCommand(() =>
{
CurrentView = new TestView1();
}));
}
}
private RelayCommand _callTestView2Command;
public RelayCommand CallTestView2Command
{
get
{
return _callTestView2Command ??
(_callTestView2Command = new RelayCommand(() =>
{
CurrentView = new TestView2();
}));
}
}
As seen each command will set CurrentView to a new view, and the views will switch in the MainView, because CurrentView will raise a ProperTyChanged Event.
This will actually work to some extend, since the CurrentView will
change but instead of actually showing the content of the view it
simply shows the Namespace of the ViewModel that is instanciated.
Because you are changing the CurrentView property to a viewmodel instance and bind that as the Content. This is wrong as the Content should be a view and you should set the DataContext of that view to a viewmodel.
The simplest thing you can do here is to create a View instance inside the command and set the viewmodel as its DataContext and then you can set the view to the CurrentView property. Of course this would violate the MVVM pattern so you should move this responsibility to a separate component. Instead of writing your own navigating logic I suggest you to pick up an existing solution as this kind of task is not as straightforward as it seems.
I suggest to use the Prism library
I have this problem. I use caliburn micro in WPF. In view I have listbox, and I bind on event MouseDoubleClick method in view-model. I would like send as parameter selected listbox item. But I don’t know how do it.
in view I have this:
<ListBox Name="Friends"
SelectedItem="Key"
Style="{DynamicResource friendsListStyle}"
Grid.Row="2"
Margin="4,4,4,4"
Micro:Message.Attach="[MouseDoubleClick]=[Action SendRp(Key)]"
PreviewMouseRightButtonUp="ListBox_PreviewMouseRightButtonUp"
PreviewMouseRightButtonDown="ListBox_PreviewMouseRightButtonDown"
MouseRightButtonDown="FriendsListBoxMouseRightButtonDown"/>
In view model I have this method:
public void SendRp(string key)
{
MessageBox.Show(key);
}
Any advance, thank.
I dont know much about caliburn but my guess is you have to write
Micro:Message.Attach="[MouseDoubleClick]=[Action SendRp(Friends.SelectedItem)]"
also you should either omit the SelectedItem="Key" or use a binding to your ViewModel like this:
SelectedItem="{Binding Key}"
In our product, we use MVVM model first approach and it works nicely but with one caveat. When view becomes complex it takes time to create it from the data template. If the view is shown and hidden frequently, it becomes slightly irritating. If using view first, it would be easy enough to cache a view if needed - but when using DataTemplate and model first, we do not have much control of view creation.
Anybody solved this problem already without switching to the view first method?
Works beautifully if using the #blindmeis idea.
The overall recipe:
Create a ContentControl or UserControl named ViewCache:
public partial class ViewCache
{
public ViewCache()
{
InitializeComponent();
Unloaded += ViewCache_Unloaded;
}
void ViewCache_Unloaded(object sender, RoutedEventArgs e)
{
Content = null;
}
private Type _contentType;
public Type ContentType
{
get { return _contentType; }
set
{
_contentType = value;
Content = ViewFactory.View(value); // use you favorite factory
}
}
}
In the DataTemplate, use the ViewCache, pass the type of the real view you want to use:
<Window.Resources>
<DataTemplate DataType="{x:Type TestViewCache:Foo}">
<TestViewCache:ViewCache ContentType="{x:Type TestViewCache:View }"/>
</DataTemplate>
</Window.Resources>
<StackPanel>
<ContentPresenter Height="200" Width="300"
Content="{Binding ViewModel}"/>
<Button Content="Set VM" Click="SetVMClick"/>
<Button Content="UnSet VM" Click="UnSetVMClick"/>
</StackPanel>
with viewmodel first approach i think you have no chance to "cache" the view. so you may consider to use view first and a viewmodel locator for the heavyweight datatemplates workflows. here is a solution when using datatemplates with lists.
but maybe there is any solution with overriding the wpf datatemplate mechanism?
edit: what if you create just a "markerview" for your viewmodel, so wpf datatemplate can find it. and then within this marker view you create/rehydrate the real view? something like an view service locator?
OK, so the situation is I'm defining an ItemTemplate for a ListBox in a ResourceDictionary (Styles.xaml). The ListBoxItem Template looks something like this:
<ControlTemplate TargetType="ListBoxItem">
<Button Command="{Binding Path=DoSomeCommand}" Content="Test" />
</ControlTemplate>
Now wherever this template is used, I'd like to have this button's click bind to an available ViewModel command to handle it.
However this does not work as is, I've also tried this:
<ControlTemplate TargetType="ListBoxItem">
<Button Command="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DoSomeCommand}" Content="Test" />
</ControlTemplate>
But still no dice.
A simple example that does work is if you define the template in the control (resources) that is using it, and just use an event handler (the same handler for all generated XAML.
Any ideas or thoughts on the best way to accomplish this? I figure this must be a common scenario: the goal is just to allow the user to interact with the items in the ListBox.
Thanks!
OK I think I answered my own question :
The solution seems to be to use 'nested' ViewModels here:
In other words, rather than have my ListBox bind directly to a collection of DTOs/business objects (as I was doing above) I instead created a simple ViewModel to wrap each DTO, and have the command on it, rather than on the original, top-level VM.
So the bound collection now looks like this:
TestItems = new ObservableCollection<ItemVM> ()
{
new ItemVM(),
new ItemVM(),
new ItemVM()
};
And each ItemVM just wraps the DTO, and has the command:
public class ItemVM : INotifyPropertyChanged
{
public ItemVM ()
{
this.MyCommand = new DelegateCommand<string> ( TheCommand );
}
public ICommand MyCommand { get; private set; }
public MyBusinessObject BizObj;
}
And voila, no need for a RelativeSource, and we have a reusable template complete with commands.
Long answer: Reference to a TextBox inside a DataTemplate
Short answer: Use Prism Commands or Blend Behaviours.