MVVM setting default values
I'm sure this is a pretty common scenario, and I would like to know how MVVM developers tackle this.
I have a ViewModel that is instantiated on demand, and persists until it is explicitly removed by the user. It's corr开发者_开发技巧esponding View is loaded onto the UI on demand. The View is able to unload and it's ViewModel may still exists in the application.
In my scenario, I have a ListBox of preset colors in the View(by setting it's ItemsSource to a Xaml-defined ObservableCollection of SolidColorBrush).
I bound the ListBox's SelectedItem property to a property in the ViewModel so that when the View is to be loaded again, the SelectedItem correctly shows the last selected item in the ListBox and also when the user selects a different color, the VM will handle the change.
My question is, how would u set the default value, say the third item in the ObservableCollection of SolidColorBrush to the ViewModel when the View is first loaded?
Usually I set the defaults in the Constructor, unless the defaults may take some time to load, in which case I'll set call a method to set it in the getter for the bound property.
The reason for this is to simplify maintenance. If I am looking for where I set the default value so I can view or change it, the first place I check is the constructor. It's easier to find than scrolling through the properties, and is known to contain initialization logic.
MyViewModel()
{
// Set defaults
SelectedColor = Brushes.Red;
}
For properties that may take longer to load, I use a method that is called in the getter for the same reason. Typically all my properties and their getters/setters are hidden in a region, and I find it much easier to find a method called LoadColors()
in my class than finding the Colors
properties in the huge list of properties I have. Also, it's reusable, so if I need to do something like reset the value, its easy to do so without repeating my code.
ObservableCollection<SolidColorBrush> Colors
{
get
{
if (_colors == null)
LoadColors();
return _colors;
}
set { ... }
}
void LoadColors()
{
// Initialization logic here
}
You can also set the default in your XAML by using the FallbackValue of the binding, however this usually only makes sense when there is a possibility of the binding's DataContext
not existing when the binding is evaluated.
<!-- You may have to look up the exact syntax for Brushes.Red -->
<ListBox SelectedItem="{Binding SelectedColor, FallbackValue=Red}" />
And last but not least, you can always resort to code-behind the view to execute view-specific logic like your example. For example,
void ComboBox_Loaded(object obj, EventArgs e)
{
if (MyComboBox.SelectedIndex == -1)
MyComboBox.SelectedIndex = 2;
}
I believe you error is in your implementation. The reason to have MVVM is to have a "separation of concerns". That makes your view just an implementation, that can get switched or updated out if/when the need arises. Once you start putting stuff in your view that is part of the application logic, you are traveling down a path of a maintenance headache, and then spaghetti code can quickly ensue.
Some people say, "Don't put any code in your view", I agree 99% of the time. I say "Don't put any domain/application/business logic in your view."
Whenever you're trying to put some code into your view ask yourself "If I switched from WPF to another framework would my app still work?" If the answer is no, then modify your ViewModel to incorporate what you were trying to put in your view.
First read Jose Answer if your answer to his question will be "yes" try this
XAML
<Window x:Class="icube.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<Grid>
<ListBox Name="myLB" Margin="0,0,445,291"
SelectedItem="{Binding mySelectedItem, UpdateSourceTrigger=PropertyChanged}"
SelectedIndex="{Binding mySelectedIndex,Mode=OneTime}" >
<ListBox.ItemTemplate>
<!-- or what ever yourTemplate will be-->
<DataTemplate DataType="{x:Type SolidColorBrush}">
<Rectangle Width="20" Height="20" Fill="{Binding}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
Code-behind
using System.Windows;
using System.Windows.Media;
using System.Collections.ObjectModel;
namespace icube
{
/// <summary>
/// Interaktionslogik für MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var obcoll = new ObservableCollection<SolidColorBrush>();
obcoll.Add(Brushes.Red);
obcoll.Add(Brushes.Green);
obcoll.Add(Brushes.Yellow);
obcoll.Add(Brushes.Blue);
obcoll.Add(Brushes.Orange);
myLB.ItemsSource = obcoll;
DataContext = new myClass();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
//importent if you want to see your selected Element
myLB.ScrollIntoView(myLB.SelectedItem);
}
}
}
myClass
public class myClass
{
private SolidColorBrush _mySelectedItem = new SolidColorBrush();
public SolidColorBrush mySelectedItem
{
get { return _mySelectedItem; }
set { _mySelectedItem = value; }
}
public int mySelectedIndex
{
get { return 4; }
}
}
summary
like you can see the default SelectedItem
get setted by the SelectedIndex
which is bounded per OneTime
Mode to mySelectedIndex
. I also show here how you could get it IntoView
i how this will help somebody.
typically In MVVM your ObservableCollection of SolidBrushColor should be defined as a property in ViewModal. and this property should be bound to your ListBox. Now, to set default value you can have it in your ViewModal's constructor
If the property bound to SelectedItem is declared as dependency property then you can set the default value of the dependency propery to whatever you want.
public static readonly DependencyProperty SelectedColorProperty
= DependencyProperty.Register("SelectedColor", typeof(SolidBrushColor ),
typeof(<yourviewmodel>),
new UIPropertyMetadata(GetDefaultColor()));
Where GetDefaultColor() is a static method in your ViewModel,
which will return the required color from your ObservableCollection .
精彩评论