I am working on a silverlight 5 existing application where MVVM approached is followed.
I have created a my own ErrorMessageBox.xaml (childwindow) in View folder and i am in situation where this ErrorMessageBox must be popuped in a class inside Model folder.
And i found that the ErrorMessageBox are not accessible in Model (because it is in View folder).So at last i created one more ErrorMessageBox.xaml inside Model so that it will be used in all
classes in Model folder.
And when i try to popup this child window(ErrorMessageBox.xaml) then it do not pop up. Why it happens and how to Pop up this ErrorMessageBox.xaml inside a function call in a class in Model
folder.
public static void ThisFunctionIsCalledIHaveVerifiedOnDebugging(string message) //it is inside a class in Model folder
{
ConfirmationWindow cfw = new ConfirmationWindow();
cfw.SetMessage("Popup test");
cfw.Show(); //it do not pop up it
}
And ConfirmationWindow.xaml is :
<silvercontrols:ChildWindow x:Class="Model.MessageFolder.ConfirmationWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:silvercontrols="clr-namespace:Silverlight.Windows.Controls;assembly=Silverlight.Windows.Controls"
xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
xmlns:toolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"
Title="Message" Width="Auto" Height="Auto" MouseRightButtonDown="ChildWindow_MouseRightButtonDown">
<silvercontrols:ChildWindow.Style>
<StaticResource ResourceKey="MessageBoxStyle"/>
</silvercontrols:ChildWindow.Style>
<Grid x:Name="LayoutRoot" MinWidth="360">
<StackPanel Orientation="Vertical">
<TextBlock x:Name="MessageBox" Margin="10 15 0 0" Height="Auto" FontSize="12" Text="Text" Foreground="White" TextWrapping="Wrap" HorizontalAlignment="Left" />
<StackPanel x:Name="ContentBox" Margin="10 15 0 0" Height="Auto" Orientation="Horizontal"></StackPanel>
<StackPanel Margin="0 0 0 10" Orientation="Horizontal" HorizontalAlignment="Center" Height="45">
<Button x:Name="YesBtn" Content="Yes" Width="82" HorizontalAlignment="Left" VerticalAlignment="Bottom" Style="{StaticResource ButtonStyle_Blue}"/>
<Button x:Name="NoBtn" Content="No" Margin="60 0 0 0" Width="82" HorizontalAlignment="Right" VerticalAlignment="Bottom" Style="{StaticResource ButtonStyle_Blue}"/>
</StackPanel>
</StackPanel>
</Grid>
</silvercontrols:ChildWindow>
and ConfirmationWindow.xaml.cs is :
using System.Windows;
namespace Model.MessageFolder
{
public partial class ConfirmationWindow : Silverlight.Windows.Controls.ChildWindow
{
private bool showBtnClose;
public ConfirmationWindow(bool showBtnClose = false)
{
InitializeComponent();
HasCloseButton = showBtnClose;
this.showBtnClose = showBtnClose;
NoBtn.Click += Close;
}
#region METHODS
public void SetMessage(string message)
{
MessageBox.Text = message;
}
public void AddContent(UIElement elt)
{
ContentBox.Children.Add(elt);
}
#endregion
#region EVENT_HANDLER
public void Close(object sender, RoutedEventArgs e)
{
this.Close();
}
#endregion
private void ChildWindow_MouseRightButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
e.Handled = true;
}
}
}
Why it is not working? How to make it work ?
First thing is that you should not bring your childwindow class in the models folder because it breaks the MVVM pattern. Instead leave it in your views folder.
What you should do is to show the childwindow from your model's view.
To achieve that you need a way to tell your view when to show the childwindow and what message it should display.
First, in your model create a property ErrorMessage:
public class MyModel : INotifyPropertyChanged
{
...
private string _errorMessage;
public string ErrorMessage
{
private set
{
_errorMessage = value;
OnPropertyChanged("ErrorMessage");
}
get { return _errorMessage;; }
}
...
}
Note: I assume here that your model class implements INotifyPropertyChanged interface but it could be a different implementation.
Then in your view's code behind add a dependency property and bind it to your model's ErrorMessage.
The dependency property's change callback is used to display the childwindow.
This could look like the following:
public partial class MyView : UserControl
{
...
public static readonly DependencyProperty ErrorMessageProperty =
DependencyProperty.Register("ErrorMessage", typeof (string), typeof (MyView),
new PropertyMetadata((o, args) =>
{
// Display childwindow when message is changed
string message = args.NewValue as string;
if(message!=null)
{
ConfirmationWindow cfw = new ConfirmationWindow();
cfw.SetMessage(message);
cfw.Show();
}
}));
public string ErrorMessage
{
get { return (string)GetValue(ErrorMessageProperty); }
private set { SetValue(ErrorMessageProperty, value); }
}
...
public MyModel ViewModel
{
...
set
{
DataContext = value;
Binding binding = new Binding();
binding.Source = value;
binding.Path = new PropertyPath("ErrorMessage");
SetBinding(ErrorMessageProperty, binding);
}
...
}
...
}
Then every time you change the value of ErrorMessage in your model it should show the childwindow.
Related
I am new to wpf format, and ran into a problem at the end of my project.
So let's say I have a top bar, with one textbox and a button.
When the user clicks the button, the user control below this bar should update with the search results from the textbox, and it does, except it does not refresh the UI, only the data storage. For simplicity I will post a demo code modelling the issue, with a single string property.
<!-- the main window -->
<Window.DataContext>
<local:CustomerViewModel/>
</Window.DataContext>
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" Grid.Row="0">
<Label Content="content of the textbox: " Margin="10"/>
<TextBox Width="300" Text="{Binding Customer.Name}"/>
<Button Content="Update" Command="{Binding UpdateCommand}"/>
</StackPanel>
<DockPanel Grid.Row="1">
<local:TestControl />
</DockPanel>
user control:
<!-- the user control named TestControl-->
<UserControl.DataContext>
<local:CustomerViewModel />
</UserControl.DataContext>
<DockPanel LastChildFill="True">
<Label DockPanel.Dock="Top" Content="Saved" />
<Label DockPanel.Dock="Bottom" Content="{Binding Info, UpdateSourceTrigger=PropertyChanged}" />
</DockPanel>
datamodel class:
public class Customer : ObservableObject
{
private string mName;
public string Name
{
get => mName;
set
{
if (value != mName)
{
mName = value;
OnPropertyChanged(nameof(Name));
}
}
}
}
viewmodel class:
public class CustomerViewModel : ObservableObject
{
private Customer mCustomer;
public Customer Customer
{
get => mCustomer;
set
{
if (value != mCustomer)
{
mCustomer = value;
OnPropertyChanged(nameof(Customer));
}
}
}
private string mInfo;
public string Info
{
get => mInfo;
set
{
if (value != mInfo)
{
mInfo = value;
OnPropertyChanged(nameof(Info));
}
}
}
private ICommand mUpdateCommand;
public ICommand UpdateCommand
{
get
{
if (mUpdateCommand == null)
{
mUpdateCommand = new RelayCommand(p => SaveChanges());
}
return mUpdateCommand;
}
}
public void SaveChanges()
{
Info = Customer.Name + " was updated";
MessageBox.Show(Info);
}
public CustomerViewModel()
{
mCustomer = new Customer();
mCustomer.Name = "Test";
Info = mCustomer.Name;
}
}
The correct value is displayed in the messagebox, but it does not change in the user control. I am calling the property changed interface, and have tried to invoke the button press with dispatcher.invoke, same issue, am I missing something very obvious here?
Your usercontrol is creating its own personal instance of the viewmodel, and using that for its DataContext. That usercontrol instance then sets Info on itself, not on the CustomerViewModel that the parent window has for its own datacontext.
<UserControl.DataContext>
<local:CustomerViewModel />
</UserControl.DataContext>
Remove those three lines from your usercontrol. Keep the corresponding lines in the window. The usercontrol will then inherit its datacontext from its parent, and they'll both be on the same page.
Those three lines aren't just declaring the type of viewmodel the view uses; they're creating an actual instance of the class and assigning it to DataContext.
What am I doing wrong?
I have a Class Model.cs that has my DataContext
I have a Button and a TextBlock next to it. I have tried binding and implementing INotifyPropertyChanged.
When the button is clicked it calls a method that uses WinForms to look for a folder location and display it in the TextBlock
but it does not update. If I debug I get the path correctly.
Any help much appreciated.
MainWindow.xaml
<Button Name="projectLocationBtn"
Width="150"
Height="30"
Click="projectLocationBtn_Click">
<StackPanel Orientation="Horizontal">
<fa:FontAwesome Icon="FolderOpen" Margin="0 0 10 0" />
<TextBlock Text="Select Location" />
</StackPanel>
</Button>
<StackPanel Orientation="Horizontal" Margin="20 0 0 0">
<fa:FontAwesome Icon="AngleRight" Margin="0 0 10 0"/>
<TextBlock Width="800"
TextAlignment="Left"
TextWrapping="NoWrap"
Text="{Binding ProjectLocation}"/>
</StackPanel>
MainWindow.xaml.cs
using M = MercuryTemplateGenerator.Model;
public MainWindow()
{
InitializeComponent();
DataContext = new M.Model();
}
private void projectLocationBtn_Click(object sender, RoutedEventArgs e)
{
M.Model m = new M.Model();
m.GetLocation();
}
Model Class
using Winforms = System.Windows.Forms;
namespace MercuryTemplateGenerator.Model
{
public class Model: INotifyPropertyChanged
{
string _projectLocation;
string _projectName;
public Model() {}
public string ProjectName
{
get {
return _projectName; }
set {
_projectName = value;
OnPropertyChanged("ProjectName");
}
}
public string ProjectLocation
{
get {
return _projectLocation; }
set {
_projectLocation = value;
OnPropertyChanged("ProjectLocation");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string property)
{
PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs(property));
}
public void GetLocation()
{
// get path to desktop
var startPath =
Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
Winforms.FolderBrowserDialog folderDialog = new
Winforms.FolderBrowserDialog();
folderDialog.ShowNewFolderButton = false;
folderDialog.SelectedPath = startPath;
Winforms.DialogResult pathResult = folderDialog.ShowDialog();
if (pathResult == Winforms.DialogResult.OK)
{
_projectLocation = folderDialog.SelectedPath;
}
}
}
}
Many thanks.
The mistake is you have one instance of Model for dataContext of the page and have another one instance you're calling inside projectLocationBtn_Click. If a view is bounded to dataContext it means it's special instance of class lays under view and view will get new data from there. You need to call GetLocation method on the same instance of Model. For example, you can save your first model to field.
_dataContext = new M.Model();
DataContext = _dataContext;
And then use this instance inside handler
private void projectLocationBtn_Click(object sender, RoutedEventArgs e)
{
_dataContext.GetLocation();
}
I can see that after all, it won't work because you don't call OnPropertyChanged("ProjectLocation").
For calling it you have to call setter of ProjectLocation property
Replace:
_projectLocation = folderDialog.SelectedPath;
with
ProjectLocation = folderDialog.SelectedPath;
And for your info: Check how can Button's click be bound to DataContext with Binding work inside XAML file.
https://www.codeproject.com/Articles/238657/How-to-use-Commands-in-WPF
In the GetLocation function you need to set the ProjectLocation property to raise the PropertyChanged event, if you set directly the _projectLocation private field the event won't be raised because it is inside the setter of the property
I have usercontrol like this :
it is header user control, i am using this control to few pages,
<Grid>
<TextBlock Grid.Column="1" Style="{StaticResource PuzzleTalkHeader}" Text="{Binding Path=LocalizedResources.GlobalApplicationTitle, Source={StaticResource LocalizedStrings}}" Grid.ColumnSpan="2"/>
<Image x:Name="imgCoin" Grid.Column="3" Height="24" Width="24" Source="/Assets/Images/Coin.png" />
<TextBlock x:Name="tbxEarnPoints" Grid.Column="5" Text="15000"/>
</Grid>
i want to set the one property like this : isVisibleEarnPoints="True"
<UserControls:Header Grid.Row="0" isVisibleEarnPoints="True"/>
if isVisibleEarnPoints="True" then i want the imgCoin and tbxEarnPoints should be visiable and if it is false, then those element hide
i am trying something like this, but i am not able get result, can you help me?
public partial class Header : UserControl
{
public Header()
{
InitializeComponent();
}
//public bool isVisiableEarnPoints
//{
// set
// {
// if(value)
// {
// imgCoin.Visibility = Visibility.Visible;
// tbxEarnPoints.Visibility = Visibility.Visible;
// }
// else
// {
// imgCoin.Visibility = Visibility.Collapsed;
// tbxEarnPoints.Visibility = Visibility.Collapsed;
// }
// }
//}
public int isVisiableEarnPoints { get; set; }
public static readonly DependencyProperty DisplayTypeProperty = DependencyProperty.Register("isVisiableEarnPoints", typeof(isVisiableEarnPoints), typeof(Header), new PropertyMetadata(YourDPCallBack));
private static void YourDPCallBack(DependencyObject instance, DependencyPropertyChangedEventArgs args)
{
Header control = (Header)instance;
}
}
}
Updated Question as per comments :
[1] :
public partial class Header : UserControl
{
public Header()
{
InitializeComponent();
}
private bool _isisVisiableEarnPoints;
public bool isVisiableEarnPoints
{
get{
return _isisVisiableEarnPoints;
}
set
{
_isisVisiableEarnPoints=value;
}
}
}
and then bind this property like this :
<Image x:Name="imgCoin" Grid.Column="3" Height="24" Width="24" Source="/Assets/Images/Coin.png" Visibility="{Binding isVisiableEarnPoints, Converter={StaticResource VisibilityConverter}}" />
<TextBlock x:Name="tbxEarnPoints" Grid.Column="5" Text="15000" Visibility="{Binding isVisiableEarnPoints, Converter={StaticResource VisibilityConverter}}" />
and used in usercontrol like this :
<UserControls:Header Grid.Row="0" isVisiableEarnPoints="False"/>
but still it is not working!
For that purpose int XAML of the controls that you are going to hide/show bind a that bool property you change and assign also Converter to every one of them, in order to convert boolean value receeved into appropriate Visibility property of the control.
here is actually concrete example of implementing custom converter:
Using a value converter to bind to Visibility in the Silverlight data grid
(it's a Silverlight, but the concept is the same)
Or simply may use BooleanToVisibilityConverter provided by framework if its behavior satisfies your requirements.
This is very simple.. But why you want to create dependency property ,, you can directly attach the viewmodel property to visibilty of controls you have.
But if you want to create a new property like IsVisibleEarnPoints then you have to create DP.
For Eg.`
public partial class YOUCLASS: UserControl
{
public YOUCLASS()
{
InitializeComponent();
}
public static readonly DependencyProperty IsVisibleEarnPointsProperty =
DependencyProperty.Register("IsVisibleEarnPoints", typeof(bool), typeof(YOURCLASS), new UIPropertyMetadata((bool)false, OnStateChange));
public bool IsVisibleEarnPoints
{
get
{
return (bool)GetValue(IsVisibleEarnPointsProperty );
}
set
{
SetValue(IsVisibleEarnPointsProperty , value);
}
}
private static void OnStateChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue is bool)
{
var source = (YOURCLASS)d;
source.SetState((bool)e.NewValue);
}
}
public void SetState(bool isDisabled)
{
if(isDisabled)
{
imgecoin.Visibility=Visibility.Visible;
//same for other control
}
else
{
// Do whatever you want.
}
}`
im building a UserControl MyUserControl that has his own ViewModel MyUserControlViewModel. MyUserControl contains 6 VehicleSelectionBlock (V1, ... V6). VehicleSelectionBlock is a UserControl i've made. it has 3 RadioButton: car, train, bus; all are of enum type Vehicle and of the same GroupName VehicleGroup.
my goal is to represent each of MyUserControl's VehicleSelectionBlocks in MyUserControlViewModel.
to make my self clear: in MyUserControlViewModel i want to be able to know&change what RadioButton is checked in every one of the 6 VehicleSelectionBlock. i think my main problem is not the converter but rather the DataContex - i'm not sure how to set it correctly for each of the controllers.
iv'e tried Binding (which is the obvious solution). i tried reading here, here , and here. unfortunately neither one helped my acheive my goal.
my code is below - im kinda new to wpf and data binding in generally. i've read almost every chapter in this tutorial but still lost sometimes.
please help me get through this and understand better the DataContex concept.
ty
MyUserContlor.xaml.cs:
namespace Project01
{
/// <summary>
/// Interaction logic for MyUserContlor.xaml
/// </summary>
public partial class MyUserContlor : UserControl
{
public MyUserContlorViewModel ViewModel { get; set; }
public MyUserContlor()
{
ViewModel = new MyUserContlorViewModel();
InitializeComponent();
this.DataContext = ViewModel;
}
private void BtnImReady_OnClick(object sender, RoutedEventArgs e)
{
//this code is irrelevant to the question
throw NotImplementedException();
}
}
}
MyUserContlor.xaml:
<UserControl x:Class="Project01.MyUserContlor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:loc="clr-namespace:Project01"
mc:Ignorable="d"
HorizontalContentAlignment="Center" VerticalContentAlignment="Center">
<Viewbox Stretch="Uniform">
<StackPanel>
<loc:VehicleSelectionBlock Name="V1"/>
<loc:VehicleSelectionBlock Name="V2"/>
<loc:VehicleSelectionBlock Name="V3"/>
<loc:VehicleSelectionBlock Name="V4"/>
<loc:VehicleSelectionBlock Name="V5"/>
<loc:VehicleSelectionBlock Name="V6"/>
<Button x:Name="BtnImReady" Click="BtnImReady_OnClick">Im Ready!</Button>
</StackPanel>
</Viewbox>
</UserControl>
MyUserContlorViewModel.cs:
namespace Project01
{
public class MyUserContlorViewModel : INotifyPropertyChanged
{
public MyUserContlorViewModel()
{
VehicleArr = new MyViewModel_Vehicle[6];
PropertyChanged+=MyUserControlViewModel_PropertyChanged;
}
public MyViewModel_Vehicle[] VehicleArr;
public event PropertyChangedEventHandler PropertyChanged;
public PropertyChangedEventHandler GetPropertyChangedEventHandler() { return PropertyChanged; }
private void MyUserControlViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
//might be useful
throw NotImplementedException();
}
}
//this class should represent a VehicleSelectionBlock
public class MyViewModel_Vehicle
{
public Vehicle VehicleSelected {get; set;}
MyViewModel_Vehicle(){}
MyViewModel_Vehicle(Vehicle v){ VehicleSelected = v;}
}
}
VehicleSelectionBlock.xaml:
<UserControl x:Class="Project01.VehicleSelectionBlock"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Project01"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
<Border VerticalAlignment="Center" HorizontalAlignment="Center" Background="GhostWhite"
BorderBrush="Gainsboro" BorderThickness="1">
<StackPanel >
<Label Content="{Binding Name}"
FontWeight="Bold" HorizontalContentAlignment="Center"></Label>
<RadioButton GroupName="VehicleGroup" >car</RadioButton>
<RadioButton GroupName="VehicleGroup">train</RadioButton>
<RadioButton GroupName="VehicleGroup" IsChecked="True">bus</RadioButton>
</StackPanel>
</Border>
</Grid>
</UserControl>
VehicleSelectionBlock.xaml.cs:
namespace Project01
{
/// <summary>
/// Interaction logic for VehicleSelectionBlock.xaml
/// </summary>
public partial class VehicleSelectionBlock : UserControl
{
public VehicleSelectionBlock()
{
InitializeComponent();
}
public VehicleSelectionBlock(String name)
{
name = Name;
InitializeComponent();
}
public static readonly DependencyProperty NameProperty = DependencyProperty.Register(
"Name", typeof (String), typeof (VehicleSelectionBlock), new PropertyMetadata(default(String)));
public String Name
{
get { return (String) GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
}
public enum Vehicle { Car, Train, Bus}
}
here is a quick solution. keep in mind that the code needs to change if you want to add more values to your Vehicle enum.
the MyUserControlViewModel.cs file
public class MyUserControlViewModel
{
public MyUserControlViewModel()
{
VehicleArr = new VehicleViewModel[6];
for (int i = 0; i < 6;i++ )
VehicleArr[i] = new VehicleViewModel();
}
public VehicleViewModel[] VehicleArr { get; set; }
}
this will expose your 6 items. They could be more. As a result they will be displayed in an ItemsControl, as you will see later.
public class VehicleViewModel:ViewModelBase
{
private bool isCar, isTrain, isBus;
public bool IsCar
{
get { return isCar; }
set
{
if (isCar == value) return;
isCar = value;
OnChanged("IsCar");
}
}
public bool IsTrain
{
get { return isTrain; }
set
{
if (isTrain == value) return;
isTrain = value;
OnChanged("IsTrain");
}
}
public bool IsBus
{
get { return isBus; }
set
{
if (isBus == value) return;
isBus = value;
OnChanged("IsBus");
}
}
}
instances of VehicleViewModel will contain your radio selection using 3 bool properties. this is the solution disadvantage. If you want more values you'll have to add more properties. you can see this inherits ViewModelBase. ViewModelBase just implements INPC so i'm not going to put it here. ViewModelBase also exposes the OnChange method that triggers the INPC event.
displaying the list can be done in your MyUserControl by using an ItemsControl like below.
<ItemsControl ItemsSource="{Binding VehicleArr}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<loc:VehicleControl />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
each item is also a UserControl. The VehicleControl user control is just a StackPanel that displays the RadioButons. This can be seen below.
<StackPanel Orientation="Horizontal">
<RadioButton Content="Car" Margin="5" VerticalAlignment="Center" IsChecked="{Binding Path=IsCar, Mode=TwoWay}"/>
<RadioButton Content="Train" Margin="5" VerticalAlignment="Center" IsChecked="{Binding Path=IsTrain, Mode=TwoWay}"/>
<RadioButton Content="Bus" Margin="5" VerticalAlignment="Center" IsChecked="{Binding Path=IsBus, Mode=TwoWay}"/>
</StackPanel>
please notice that each RadioButton is bound to one of the 3 properties in the VehicleViewModel instance.
Once you press your button you should have all the selections recorded. if you want you could have a function that returns an enum value by analysing the 3 bool properties if that is what you need.
the best solution will be to get rid of the radio buttons and replace them with combo boxes. in this way you can change the enum members and everything will continue to work without changing anything else. this might look as below.
public class VehicleViewModel:ViewModelBase
{
private Vehicle selOption;
private readonly Vehicle[] options;
public VehicleViewModel()
{
this.options = (Vehicle[])Enum.GetValues(typeof(Vehicle));
}
public Vehicle[] Options { get { return options; } }
public Vehicle SelectedOption
{
get { return selOption; }
set
{
if (selOption == value) return;
selOption = value;
OnChanged("SelectedOption");
}
}
}
and for the view:
<ItemsControl ItemsSource="{Binding VehicleArr}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Options}"
SelectedItem="{Binding SelectedOption, Mode=TwoWay}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
You can do directly in the code-behind of your control (in the default constructor)
public VehicleSelectionBlock()
{
InitializeComponent();
this.DataContext = new MyUserContlorViewModel ();
}
You can also do that in XAML (http://msdn.microsoft.com/en-us/library/ms746695(v=vs.110).aspx) declaration, as you wish.
I want to develop dialog for editing objects that make use of polymorphism. Currently I'm using this pattern:
MyObject.cs:
using System;
namespace WpfApplication3
{
public class MyObject
{
public string Title { get; set; }
public MySettings Settings { get; set; }
}
public abstract class MySettings
{
public abstract string GetSettingsString();
}
public class MyBoolSettings : MySettings
{
public bool BoolSetting { get; set; }
public override string GetSettingsString()
{
return "BoolSetting = " + BoolSetting;
}
}
public class MyStringSettings : MySettings
{
public string StringSetting { get; set; }
public override string GetSettingsString()
{
return "StringSetting = " + StringSetting;
}
}
}
MainWindow.xaml:
<Window x:Class="WpfApplication3.EditMyObjectDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="EditMyObjectDialog" Height="350" Width="350">
<StackPanel Margin="20">
<TextBlock Text="Title" />
<TextBox Name="txtTitle" />
<RadioButton Name="rdBoolSettings" Content="BoolSettings" IsChecked="True" Margin="0, 20, 0, 0" />
<CheckBox Name="chBool" Content="True" Margin="20, 0, 0, 20" />
<RadioButton Name="rdStringSettings" Content="StringSettings" />
<TextBox Name="txtString" Margin="20, 0, 0, 20"/>
<Button Content="OK" Click="OK_click" />
<Button Content="Cancel" Click="Cancel_click" Margin="0, 10" />
</StackPanel>
</Window>
MainWindow.xaml.cs:
using System.Windows;
namespace WpfApplication3
{
public partial class EditMyObjectDialog : Window
{
public MyObject Result { get; set; }
public EditMyObjectDialog(MyObject objectToEdit)
{
InitializeComponent();
txtTitle.Text = objectToEdit.Title;
if (objectToEdit.Settings is MyBoolSettings)
{
rdBoolSettings.IsChecked = true;
chBool.IsChecked = (objectToEdit.Settings as MyBoolSettings).BoolSetting;
}
if (objectToEdit.Settings is MyStringSettings)
{
rdBoolSettings.IsChecked = true;
txtString.Text = (objectToEdit.Settings as MyStringSettings).StringSetting;
}
}
private void OK_click(object sender, RoutedEventArgs e)
{
Result = new MyObject() { Title = txtTitle.Text };
if (rdBoolSettings.IsChecked == true)
Result.Settings = new MyBoolSettings() { BoolSetting = chBool.IsChecked == true };
if (rdStringSettings.IsChecked == true)
Result.Settings = new MyStringSettings() { StringSetting = txtString.Text };
DialogResult = true;
}
private void Cancel_click(object sender, RoutedEventArgs e)
{
DialogResult = false;
}
}
}
ExternalCode:
var f = new EditMyObjectDialog(myObject);
if (f.ShowDialog() == true)
myObject = f.Result;
I belive there is much better design pattern that uses data binding etc. So basically I have two questions.
How to make data binding not to
modify object until user hits 'OK'?
How to correctly handle 'Settings'
property? What to do when user
switches setting's type?
What I believe you're looking for is a combination of DataBinding and DataTemplating. DataTemplating will allow you to define different visual elements for different business objects (in this case MyBooleanSettings and MyStringSettings. DataBinding will allow the visual elements to update and be updated my the data in the business objects.
Example (xaml):
<Window DataContext={Binding RelativeSource={RelativeSource Self}}">
<Window.Resources>
<DataTemplate DataType={x:Type local:MyObject}">
<TextBlock Text={Binding Title}" />
<ContentPresenter Content="{Binding Settings}" />
</DataTemplate>
<DataTemplate DataType={x:Type local:MyObject}">
<TextBox Text={Binding
</DataTemplate>
<DataTemplate DataType={x:Type local:MyBoolSettings}>
<CheckBox IsChecked="{Binding BoolSetting}" />
</DataTemplate>
<DataTemplate DataType={x:Type local:MyStringSettings}>
<TextBox Text="{Binding StringSetting}" />
</DataTemplate>
</Window.Resources>
<ContentPresenter Content="{Binding ObjectToEdit}" />
</Window>
Then in the code behind define:
public MyObject ObjectToEdit { get; set; }
Finally update your objects:
public class MySettings : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(sting s)
{
if(PropertyChanged != null)
{
PropertyChanged(s);
}
}
}
public class BoolSettings : MySettings
{
bool _value;
bool BoolSetting
{
get { return _value; }
set
{
if(_value != value)
{
_value = value;
OnPropertyChanged("BoolSetting");
}
}
}
}
If however you really need to control when the view and object sync you should use the UpdateSourceTrigger property on the corresponding bindings.
If you want some additional reading I recommend: http://msdn.microsoft.com/en-us/library/ms752347.aspx
DataBinding is Simple . You can create an instance of MyObject and assign it to the DataContext property of the Form.
this.DataContext=MyObject;
And define binding for individual elements.
<TextBox Name="txtTitle" Text="{Binding Path=Title,Mode=TwoWay }" />
Setting mode as two way will affect the object as you make change in UI. One way will show the values.
How to make data binding not to modify object until user hits 'OK'?
Create a copy of the MyObject instance. In the Result property get method, return copy if user hit cancel (return unchanged copy) or if user hit OK, return the changed MyObject instance.
How to correctly handle 'Settings' property? What to do when user switches setting's type?
Whats the problem?