Prism 6.2 Binding to Model not ViewModel - c#

I am trying to use Prism in a C# to but I seem to have set it up so that it binds to items in my model and not my viewmodel. It is a short program that is more of a learning tool that anything. When I move the items to the Viewmodel the SetProperty's don't seem to notify the view of the change.
Any thoughts as to how I have this setup backwards?
Model:
namespace XMLValueModifier_Threaded_WPF.Models
{
public class XMLReadFileModel : BindableBase
{
private string _XMLMasterFile2 = "0";
public string XMLGetFileName()
{
if (_XMLMasterFile2 != "1")
{
Microsoft.Win32.OpenFileDialog _XMLMasterFileDialog = new Microsoft.Win32.OpenFileDialog();
_XMLMasterFileDialog.DefaultExt = "xml";
_XMLMasterFileDialog.Filter = "xml Files (*.xml; *.XML) | *.xml; *.XML";
Nullable<bool> result = _XMLMasterFileDialog.ShowDialog();
if (result == true)
{
return _XMLMasterFileDialog.FileName;
}
return "";
}
else
{
return "";
}
}
}
}
ViewModel:
namespace XMLValueModifier_Threaded_WPF.ViewModels
{
public class MainDialogueViewModel : BindableBase
{
private string _XMLMasterFile;
public ICommand MasterFileLocation
{
get;
set;
}
public ICommand XMLFileLocation
{
get;
set;
}
public string XMLMasterFile
{
get
{
return _XMLMasterFile;
}
set
{
SetProperty(ref _XMLMasterFile, value);
}
}
private XMLReadFileModel xmlReadFileModel = new XMLReadFileModel();
public MainDialogueViewModel()
{
XMLReadFileModel xmlReadFileModel = new XMLReadFileModel();
Message = "example message";
XMLMasterFile = "example File";
this.MasterFileLocation = new DelegateCommand(chooseFile, canChooseFile);
this.XMLFileLocation = new DelegateCommand(chooseFile, canChooseFile);
}
public void masterfilelocation()
{
MessageBox.Show("i am here");
return;
}
private void chooseFile()
{
XMLMasterFile = xmlReadFileModel.XMLGetFileName();
}
private bool canChooseFile()
{
if (XMLMasterFile != null)
{
return true;
}
else
{
return true;
}
}
}
}
XAML:
<Window x:Class="XMLValueModifier_Threaded_WPF.Views.MainDialogue"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:XMLValueModifier_Threaded_WPF.ViewModels" Width="625" Height="452"
>
<Grid Margin="0,-24,0,-3">
<TextBox x:Name="Textbox1" Text="{Binding MainDialogueViewModel.XMLMasterFile,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Height="23" Margin="25,120,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="425"/>
<TextBox x:Name="Textbox2" Text="{Binding MainDialogueViewModel.XMLFiles,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Height="23" Margin="25,188,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="425" RenderTransformOrigin="0.506,1.565"/>
<Label Content="Location of Master XML File" HorizontalAlignment="Left" Margin="25,89,0,0" VerticalAlignment="Top"/>
<Label Content="Location of XML File(s)" HorizontalAlignment="Left" Margin="25,157,0,0" VerticalAlignment="Top"/></GRID>

Assuming you have your DataContext setup correctly to an instance of MainDialogueViewModel; you don't need to include MainDialogueViewModel in your binding. Simply bind to the property name XMLMasterFile. Also keep in mind that if the value isn't different, then nothing will be updated.

Related

Binding to an enum inside a child view model combobox object is not renders on UI

I have a Parent ViewModel which contains a child view model object inside with some enum,
When I open the UI I see that the enum value not renders as expected from the RaisePropertyChanged event on first time loading, however, after setting a value from the UI the value changes and renders as expected.
Parent VM xaml
<UserControl x:Class="RemoteDebugger.App.Views.ConfigurationControl"
d:DataContext="{d:DesignInstance viewModels:ConfigurationViewModel}">
<Grid>
//Some properties...
<local:ProjectEditor Grid.Row="5" Grid.Column="1" BorderBrush="#FFCFCBCB" BorderThickness="0,1,0,1" Grid.ColumnSpan="3" DataContext="{Binding MainProject,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" ></local:ProjectEditor>
</Grid>
Parent VM cs
using RemoteDebugger.App.Utils;
using System.Collections.Generic;
using System.IO;
using System.Windows.Input;
using Microsoft.Win32;
using RemoteDebugger.Model;
using RemoteDebugger.Model.DataStructures;
namespace RemoteDebugger.App.ViewModels
{
public class ConfigurationViewModel : BaseViewModel
{
private ICommand _saveConfiguration;
private ICommand _loadConfiguration;
private Configuration _configuration;
private ProjectViewModel _mainProject;
private ExtraProjectsViewModel _extraProjects;
public ConfigurationViewModel()
{
_configuration = RemoteDebuggerManager.Instance.Configuration;
_mainProject = new ProjectViewModel(_configuration.MainProject);
_extraProjects = new ExtraProjectsViewModel(_configuration.ExtraProjectsToCopy);
}
public ICommand SaveConfiguration
{
get
{
return _saveConfiguration ??= new Command(o =>
{
_configuration.MainProject = _mainProject.Project;
_configuration.ExtraProjectsToCopy = _extraProjects.Projects;
RemoteDebuggerManager.Instance.SaveConfigurations();
});
}
}
public ICommand LoadConfiguration
{
get
{
return _loadConfiguration ??= new Command(o =>
{
var fd = new OpenFileDialog
{
Multiselect = false,
Filter = "XML Files (*.xml)|*.xml",
InitialDirectory = ConfigurationHandler.ConfigurationFolder
};
var path = fd.ShowDialog();
if (path == null || !File.Exists(fd.FileName))
return;
_configuration = RemoteDebuggerManager.Instance.LoadConfigurations(fd.FileName);
UpdateView();
});
}
}
private void UpdateView()
{
OsBits = _configuration.OsBits;
VisualStudioVersion = _configuration.VisualStudioVersion;
CopyExtraPaths = _configuration.CopyExtraPaths;
Ip = _configuration.ControllerIp;
_mainProject.Project = _configuration.MainProject;
_extraProjects.Projects = _configuration.ExtraProjectsToCopy;
}
public string VisualStudioVersion
{
get => _configuration.VisualStudioVersion;
set
{
_configuration.VisualStudioVersion = value;
RaisePropertyChanged(nameof(VisualStudioVersion));
}
}
public string OsBits
{
get => _configuration.OsBits;
set
{
_configuration.OsBits = value;
RaisePropertyChanged(nameof(OsBits));
}
}
public string Ip
{
get => _configuration.ControllerIp;
set
{
_configuration.ControllerIp = value;
RaisePropertyChanged(nameof(Ip));
}
}
public string WaitForDebugFileDestination
{
get => _configuration.WaitForDebugFileDestination;
set
{
_configuration.WaitForDebugFileDestination = value;
RaisePropertyChanged(nameof(WaitForDebugFileDestination));
}
}
public bool CopyExtraPaths
{
get => _configuration.CopyExtraPaths;
set
{
_configuration.CopyExtraPaths = value;
RaisePropertyChanged(nameof(CopyExtraPaths));
}
}
public ProjectViewModel MainProject
{
get => _mainProject;
set
{
_mainProject = value;
RaisePropertyChanged(nameof(MainProject));
}
}
public ExtraProjectsViewModel ExtraProjects
{
get => _extraProjects;
set
{
_extraProjects = value;
RaisePropertyChanged(nameof(ExtraProjects));
}
}
public List<string> VisualStudioSupportedVersions => EnvironmentValues.VisualStudioVersions;
public List<string> OsBitsTypes => EnvironmentValues.OsBits;
}
}
Child VM xaml
<UserControl x:Class="RemoteDebugger.App.Views.ProjectEditor"
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:viewModels="clr-namespace:RemoteDebugger.App.ViewModels"
xmlns:local="clr-namespace:RemoteDebugger.App.Views"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance viewModels:ProjectViewModel}"
>
<Grid>
<Grid.RowDefinitions>
...
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
...
</Grid.ColumnDefinitions>
<ComboBox Grid.Row ="1" Grid.Column="1" ItemsSource="{Binding ProjectTypes}" VerticalAlignment="Center" SelectedItem="{Binding ProjectType,UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" FontSize="14" />
<TextBlock Grid.Row ="1" Grid.Column="0" Text="Project type" TextWrapping="Wrap"/>
<TextBlock Grid.Row ="5" Grid.Column="0" Text="Local project path" VerticalAlignment="Center" TextWrapping="Wrap"/>
<TextBox Grid.Row ="5" Grid.Column="1" Height="30" Text="{Binding LocalDllDirRoot,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Grid.Row ="7" Grid.Column="0" Text="Remote project path" VerticalAlignment="Center" TextWrapping="Wrap"/>
<TextBox Grid.Row ="7" Grid.Column="1" Height="30" Text="{Binding RemoteDllDirRoot,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Grid.Row ="9" Grid.Column="0" Text="Arguments" VerticalAlignment="Center" TextWrapping="Wrap"/>
<TextBox Grid.Row ="9" Grid.Column="1" Height="30" Text="{Binding Arguments,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Grid.Row ="3" Grid.Column="0" Text="Executable name" VerticalAlignment="Center" TextWrapping="Wrap"/>
<TextBox Grid.Row ="3" Grid.Column="1" Height="30" Text="{Binding ExecutableName,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
</Grid>
Child VM cs file
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using IntelGenericUIFramework.Common;
using RemoteDebugger.Model.DataStructures;
namespace RemoteDebugger.App.ViewModels
{
public class ProjectViewModel : BaseViewModel,ICloneable
{
private ProjectDescriptor _project;
public ProjectViewModel()
{
_project = new ProjectDescriptor();
}
public ProjectViewModel(ProjectDescriptor projectDescriptor)
{
_project = projectDescriptor == null ? new ProjectDescriptor() :
(ProjectDescriptor)projectDescriptor.Clone() ;
UpdateProperties();
}
private void UpdateProperties()
{
foreach (var prop in this.GetType().GetProperties())
{
RaisePropertyChanged(nameof(prop.Name));
}
}
#region Main project values
public string LocalDllDirRoot
{
get => _project.LocalDllDirRoot;
set
{
Project.LocalDllDirRoot = value;
RaisePropertyChanged(nameof(LocalDllDirRoot));
RaisePropertyChanged(nameof(Project));
}
}
public string RemoteDllDirRoot
{
get => _project.RemoteDllDirRoot;
set
{
_project.RemoteDllDirRoot = value;
RaisePropertyChanged(nameof(RemoteDllDirRoot));
RaisePropertyChanged(nameof(Project));
}
}
public string ExecutableName
{
get => Project.ExecutableName;
set
{
_project.ExecutableName = value;
RaisePropertyChanged(nameof(ExecutableName));
RaisePropertyChanged(nameof(Project));
}
}
public string Arguments
{
get => Project.Arguments;
set
{
_project.Arguments = value;
RaisePropertyChanged(nameof(Arguments));
RaisePropertyChanged(nameof(Project));
}
}
public bool IsService
{
get => Project.IsService;
set
{
_project.IsService = value;
RaisePropertyChanged(nameof(IsService));
RaisePropertyChanged(nameof(Project));
}
}
public ProjectType ProjectType
{
get => Project.ProjectType;
set
{
_project.ProjectType = value;
RaisePropertyChanged(nameof(ProjectType));
RaisePropertyChanged(nameof(Project));
}
}
#endregion
public void Clear()
{
LocalDllDirRoot = string.Empty;
RemoteDllDirRoot = string.Empty;
ExecutableName = string.Empty;
IsService = false;
Arguments = string.Empty;
ProjectType = ProjectType.None;
}
public ProjectDescriptor Project
{
get => _project;
set
{
if (value == null)
return;
_project = (ProjectDescriptor)value.Clone();
UpdateProperties();
}
}
public object Clone()
{
return Project.Clone();
}
public List<string> ProjectTypes => Enum.GetNames(typeof(ProjectType)).ToList();
}
}
Example of the broken binding
the marked field not loading the correct selected enum value and show a blank selection, while the other properties are shown as expected.
The problem is that 'ProjectType' property in Child VM cs file
not renders on startup in UI in Parent VM xaml
Thanks ahead!
ProjectTypes is a List<string> and ProjectType is a ProjectType.
The types should match so either change the type of the source collection:
public List<ProjectType> ProjectTypes =>
Enum.GetValues(typeof(ProjectType)).OfType<ProjectType>().ToList();
...or the type of the SelectedItem source property to string.

Binding value from parent Page ViewModel to UserControl

I write new WPF MVVM app.
I new in WPF.
I have problem with binding value to StageControl from MainPageModelView.
StageControl is in MainPage.
I know how binding value to element in MainPage, but I can't binding value to StageControl in this same way.
How can I binding value from MainPageModelView to StageControl?
Code:
MainPage.xaml
<my:StageControl x:Name="stageControl1" StageIsActive="true" StageName="{Binding Stage.Name}" Grid.Row="0" Grid.Column="0"/>
...
<Label x:Name="lbTest" Content="{Binding Test}" HorizontalAlignment="Left" Margin="104,10,0,0" VerticalAlignment="Top" Height="56" Width="68"/>
StageControl.xaml.cs
public partial class StageControl : UserControl
{
string stageName;
bool stageIsActive;
public StageControl()
{
InitializeComponent();
}
public bool StageIsActive
{
get { return this.stageIsActive; }
set { this.stageIsActive = SetStageControlStatus(value); }
}
public string StageName
{
get { return this.stageName; }
set { this.stageName = SetStageName(value); }
}
private bool SetStageControlStatus(bool value)
{
if (value)
{
this.outRing.Visibility = Visibility.Visible;
return true;
}
else
{
this.outRing.Visibility = Visibility.Hidden;
return false;
}
}
private string SetStageName(string value)
{
this.text.Text = value;
return this.text.Text;
}
}
MainPageViewModel.cs
class MainPageViewModel
{
public List<Stage> Stages = new List<Stage>();
public Stage stage = new Stage(0, "Test", true);
public MainPageViewModel()
{
Stages = Stage.GetStages();
}
public string Test
{
get { return "Testowy Label"; }
set { }
}
}
Edit:
MainPage.xaml.css
public MainPage()
{
InitializeComponent();
MainPageViewModel viewModel = new MainPageViewModel();
this.DataContext = viewModel;
}
I solved the problem.
First I add dependency property to StageControl.xaml.cs, then I add binding to StageControl.xaml
...
x:Name="Stage"
...
<TextBlock x:Name="text" TextWrapping="Wrap" Text="{Binding ElementName=Stage, Path=StageName}" TextAlignment="Center" FontSize="12" HorizontalAlignment="Center" VerticalAlignment="Center"/>
public Stage Stage {get;set;} = new Stage(0,"Test",true);
u need to make property instead of public variable

Get checked items from a listbox

I'm just getting used to MVVM and want to do without the code-behind and define everything in the view-models.
the combobox represents several selection options (works). I would like to query the elements that have been checked.
Unfortunately I can't access them. The textbox should display all selected elements as concatenated string.
View-Model
class MainViewModel : BaseViewModel
{
#region Fields
private ObservableCollection<EssayTypeViewModel> _essayTypes;
private EssayTypeViewModel _selectedEssayTypes;
#endregion
public ObservableCollection<EssayTypeViewModel> EssayTypes
{
get => _essayTypes;
set
{
if (_essayTypes == value) return;
_essayTypes = value; OnPropertyChanged("EssayTypes");
}
}
public EssayTypeViewModel SelectedEssayTypes
{
get => _selectedEssayTypes;
set { _selectedEssayTypes = value; OnPropertyChanged("SelectedEssayTypes"); }
}
public MainViewModel()
{
// Load Essay Types
EssayTypeRepository essayTypeRepository = new EssayTypeRepository();
var essayTypes = essayTypeRepository.GetEssayTypes();
var essayTypeViewModels = essayTypes.Select(m => new EssayTypeViewModel()
{
Text = m.Text
});
EssayTypes = new ObservableCollection<EssayTypeViewModel>(essayTypeViewModels);
}
}
XAML
<ListBox x:Name="Listitems" SelectionMode="Multiple" Height="75" Width="200" ItemsSource="{Binding EssayTypes}" >
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Text}" IsChecked="{Binding Checked}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBox Text="{Binding Path=SelectedEssayTypes}" Grid.Column="0" Width="160" Height="25" Margin="0,140,0,0"/>
You could hook up an event handler to the PropertyChanged event of all EssayTypeViewModel objects in the EssayTypes collection and raise the PropertyChanged event for a read-only property of the MainViewModel that returns all selected elements as concatenated string:
public MainViewModel()
{
// Load Essay Types
EssayTypeRepository essayTypeRepository = new EssayTypeRepository();
var essayTypes = essayTypeRepository.GetEssayTypes();
var essayTypeViewModels = essayTypes.Select(m =>
{
var vm = EssayTypeViewModel()
{
Text = m.Text
};
vm.PropertyChanged += OnPropertyChanged;
return vm;
});
EssayTypes = new ObservableCollection<EssayTypeViewModel>(essayTypeViewModels);
}
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Checked")
OnPropertyChanged("SelectedItems");
}
public string SelectedItems => string.Join(",", EssayTypes.Where(x => x.Checked).ToArray());
This requires the EssayTypeViewModel class to implement the INotifyPropertyChanged interface (by for example deriving from your BaseViewModel class).
You can apply Mode = Two way on the checkbox binding.
<CheckBox Content="{Binding Text}" IsChecked="{Binding Checked, Mode=TwoWay}"/>
then you can iterate through the essay types collection to check if the item entry was checked.
For ex. Sample code can be:
foreach (var essayTypeInstance in EssayTypes)
{
if(essayTypeInstance.Checked)
{
// this value is selected
}
}
Hope this helps.
mm8 answer works. In the meantime i came up with another approach. Not 100% MVVM compatible but it works and is quite simple.
XAML
<ListBox x:Name="ListItems" SelectionMode="Multiple" Height="75" Width="200" ItemsSource="{Binding CollectionOfItems}" >
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Name}" IsChecked="{Binding Checked, Mode=TwoWay}" Unchecked="GetCheckedElements" Checked="GetCheckedElements" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBox Text="{Binding SelectedItemsString, UpdateSourceTrigger=PropertyChanged}" Grid.Column="0" Width="160" Height="25" Margin="0,140,0,0"/>
Code Behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
private void GetCheckedElements(object sender, RoutedEventArgs e)
{
(DataContext as MainViewModel)?.FindCheckedItems();
(DataContext as MainViewModel)?.ConcatSelectedElements();
}
}
Model
public class Items
{
public bool Checked { get; set; }
public string Name { get; set; }
}
ItemsViewModel (BaseViewModel only implements INotifyPropertyChanged)
class ItemsViewModel : BaseViewModel
{
private bool _checked;
private string _name;
public bool Checked
{
get => _checked;
set
{
if (value == _checked) return;
_checked = value;
OnPropertyChanged("Checked");
}
}
public string Name
{
get => _name;
set
{
if (value == _name) return;
_name = value;
OnPropertyChanged("Name");
}
}
}
MainViewModel
public class MainViewModel : BaseViewModel
{
private string _selectedItemsString;
private ObservableCollection<Items> _selectedItems;
public ObservableCollection<Items> CollectionOfItems { get; set; }
public ObservableCollection<Items> SelectedItems
{
get => _selectedItems;
set
{
_selectedItems = value;
OnPropertyChanged("SelectedItems");
}
}
public string SelectedItemsString
{
get => _selectedItemsString;
set
{
if (value == _selectedItemsString) return;
_selectedItemsString = value;
OnPropertyChanged("SelectedItemsString");
}
}
public MainViewModel()
{
CollectionOfItems = new ObservableCollection<Items>();
SelectedItems = new ObservableCollection<Items>();
CollectionOfItems.Add(new Items { Checked = false, Name = "Item 1" });
CollectionOfItems.Add(new Items { Checked = false, Name = "Item 2" });
CollectionOfItems.Add(new Items { Checked = false, Name = "Item 3" });
}
public void FindCheckedItems()
{
CollectionOfItems.Where(x => x.Checked).ToList().ForEach(y => SelectedItems.Add(y));
}
public void ConcatSelectedElements()
{
SelectedItemsString = string.Join(", ", CollectionOfItems.Where(x => x.Checked).ToList().Select(x => x.Name)).Trim();
}
}

Combox SelectedItem does not apply when restoring from serialized ViewModel

I'm facing a strange problem when using C# WPF and MVVM Pattern while restoring a ViewModel (serialized using Json.Net).
The idea of the software is - when closing the window - to persist the current Viewmodel state in a json file.
At the next startup the app just serarches for the json.
If there a file, then deserialize it and restore the ViewModel (set public properties).
If there is no file, then the viewmodel is created and default values are set.
Now my problem is, that when restoring it with the json file, a combobox containing a list of a custom type, the combobox has values but no SelectedItem. When creating the viewmodel instance and initiailizing the public properties with default values (doing this via the code behind) then everything is fine.
Here is some code that represents the "error":
View
<Window x:Class="CrazyWpf.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:CrazyWpf"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
Closing="Window_Closing"
Loaded="Window_Loaded">
<StackPanel
x:Name="rootElement"
Orientation="Vertical"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Margin="10">
<StackPanel.DataContext>
<local:DemoViewModel />
</StackPanel.DataContext>
<StackPanel
Orientation="Horizontal">
<Label
x:Name="lblID"
Width="30"
Content="ID:"/>
<TextBox
x:Name="tbID"
Width="50"
Margin="30,0,0,0"
Text="{Binding ID, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
<StackPanel
Orientation="Horizontal">
<Label
x:Name="lblName"
Width="45"
Content="Name:"/>
<TextBox
x:Name="tbName"
Width="200"
Margin="15,0,0,0"
Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
<StackPanel
Orientation="Horizontal">
<Label
x:Name="lblStai"
Width="60"
Content="Status:"/>
<ComboBox
x:Name="cbStati"
Width="200"
ItemsSource="{Binding StatusTypeList}"
SelectedItem="{Binding StatusType, UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="Name"/>
</StackPanel>
</StackPanel>
</Window>
Code Behind
using System;
using System.Windows;
using System.IO;
using Newtonsoft.Json;
namespace CrazyWpf
{
public partial class MainWindow : Window
{
private DemoViewModel dvm;
public MainWindow()
{
InitializeComponent();
this.dvm = (DemoViewModel)this.rootElement.DataContext;
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
string filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "settings.json");
if (File.Exists(filePath))
File.Delete(filePath);
File.WriteAllText(filePath, JsonConvert.SerializeObject(this.dvm, Formatting.Indented));
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
string filePath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "settings.json");
if (!File.Exists(filePath))
{ this.SetDefaultSettings(); return; }
DemoViewModel d = JsonConvert.DeserializeObject<DemoViewModel>(File.ReadAllText(filePath));
this.dvm.ID = d.ID;
this.dvm.Name = d.Name;
this.dvm.StatusType = d.StatusType;
}
}
}
BaseViewModel:
using System.ComponentModel;
namespace CrazyWpf
{
public abstract class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
ViewModel
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
namespace CrazyWpf
{
class DemoViewModel : BaseViewModel
{
[JsonIgnore]
private int id;
[JsonProperty(Order = 1)]
public int ID
{
get { return this.id; }
set
{
if (this.id != value)
{
this.id = value;
this.OnPropertyChanged("ID");
}
}
}
[JsonIgnore]
private string name;
[JsonProperty(Order = 2)]
public string Name
{
get { return this.name; }
set
{
if (this.name != value && value != null)
{
this.name = value;
this.OnPropertyChanged("Name");
}
}
}
[JsonIgnore]
private StatusTyp statusType;
[JsonProperty(Order = 3)]
public StatusTyp StatusType
{
get { return this.statusType; }
set
{
if (this.statusType != value && value != null)
{
this.statusType = value;
this.OnPropertyChanged("StatusType");
}
}
}
[JsonIgnore]
private List<StatusTyp> statusTypeList;
[JsonProperty(Order = 4)]
public List<StatusTyp> StatusTypeList
{
get { return this.statusTypeList; }
set
{
if (this.statusTypeList != value && value != null)
{
this.statusTypeList = value;
this.OnPropertyChanged("StatusTypeList");
}
}
}
public DemoViewModel()
{
this.StatusTypeList = new Func<List<StatusTyp>>(() =>
{
var list = Enum.GetValues(typeof(Status))
.Cast<Status>()
.ToDictionary(k => (int)k, v => v.ToString())
.Select(e => new StatusTyp()
{
Value = e.Key,
Name = e.Value,
Status =
Enum.GetValues(typeof(Status))
.Cast<Status>().
Where(x =>
{
return (int)x == e.Key;
}).FirstOrDefault()
})
.ToList();
return list;
})();
}
}
public class StatusTyp
{
public int Value { get; set; }
public string Name { get; set; }
public Status Status { get; set; }
}
public enum Status
{
NotDetermined = 0,
Determined = 1,
Undeterminded = 2,
Unknown = 3
}
}
If you have an ItemsSource and a SelectedItem, the instance in SelectedItem MUST BE in the collection bound to ItemsSource. If it is not, then your bindings will not work as expected.
The control uses reference equality to determine which item in ItemsSource is the one in SelectedItem and update the UI. This normally isn't a problem as the control populates SelectedItem for you, but if you are updating from the ViewModel side, you have to make sure your references are managed correctly.
This can be an issue when serializing/deserializing your view model. Most common serializers don't track references, and so cannot restore these on deserialization. The same object may be referenced multiple places in the original object graph, but after deserialization you now have multiple instances of the original spread throughout the rehydrated graph. This won't work with your requirements.
What you have to do is, after deserializing, find the matching instance in your collection and substitute it for the instance in SelectedItem. Or, use a serializer that tracks instances.. The XAML serializer already does this, and is a surprisingly good xml serializer for .net object graphs.

How do you get text from a textbox with databinding propertynotifying thingy mvvm light

I have a username and password box.
Underneath it I have a button.
When I click that button I want to analyse what has been put into the username and password box.
How do I do this with mvvm light?
This is where I am:
XAML
...DataContext="{Binding Main, Source={StaticResource Locator}}">...
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0">
<TextBlock HorizontalAlignment="Left" Margin="10,0,0,0" TextWrapping="Wrap" Text="Username" VerticalAlignment="Top"/>
<TextBox HorizontalAlignment="Left" Height="72" Margin="0,27,0,0" TextWrapping="Wrap" Text="{Binding Username}" VerticalAlignment="Top" Width="456"/>
<TextBlock HorizontalAlignment="Left" Margin="10,99,0,0" TextWrapping="Wrap" Text="Password" VerticalAlignment="Top"/>
<PasswordBox HorizontalAlignment="Left" Height="72" Margin="0,126,0,0" Password="{Binding Password}" VerticalAlignment="Top" Width="456"/>
<Button Content="Log in" HorizontalAlignment="Center" Margin="167,203,169,0" VerticalAlignment="Top" Command="{Binding LogInCommand}"/>
</Grid>
View Model
public class MainViewModel : ViewModelBase
{
public LoginCredentials LoginCredentials { get; set; }
public ICommand LogInCommand { get; private set; }
public MainViewModel()
{
LoginCredentials = new LoginCredentials();
LogInCommand = new RelayCommand(this.OnLogInCommand);
}
private void OnLogInCommand()
{
string testUsername = Username;
string testPassword = Password;
}
#region Properties
public string Username
{
get { return LoginCredentials.Username; }
set { LoginCredentials.Password = value; }
}
public string Password
{
get { return LoginCredentials.Password; }
set { LoginCredentials.Password = value; }
}
#endregion
}
MainPage.xaml.cs
public partial class MainPage : PhoneApplicationPage
{
public MainPage()
{
InitializeComponent();
}
}
what is happening at the moment:
When I click my button, the LogInCommand is run and it fires my method OnLoginCommand. I have put a break point on the testUsername declaration to see if when the button is clicked, the username and password reflect what has been put in; they are both empty. What must I do to make sure these are updated as someone is typing or when the button is pressed or however it works???
I have now spent about 4 weeks learning mvvm and trying to get a simple click event and binding to work. This is simply not making sense... doh. Thanks for any help!
P.S - Is MVVM light too confusing for new comers? the documentation is so.. light on detail. No examples :(
View
Windows Phone doesn't contain "UpdateSourceTrigger=PropertyChanged". You have to use "Explicit" and manually call "UpdateSource" in code behind, otherwise value of TextBox/PasswordBox will be raise when TextBox/PasswordBox lost focus.
And don't forget to set "Mode=TwoWay".
<TextBox
Text="{Binding Path=Username, Mode=TwoWay, UpdateSourceTrigger=Explicit}"
TextChanged="TextBoxTextChanged" />
<PasswordBox
Password="{Binding Path=Password, Mode=TwoWay, UpdateSourceTrigger=Explicit}"
PasswordChanged="PasswordBoxPasswordChanged" />
<Button
Command="{Binding Path=LogInCommand}"
Content="Log in" />
View - code behind
private void PasswordBoxPasswordChanged(object sender, RoutedEventArgs e)
{
PasswordBox pb = sender as PasswordBox;
if (pb != null)
{
pb.GetBindingExpression(PasswordBox.PasswordProperty).UpdateSource();
}
}
private void TextBoxTextChanged(object sender, TextChangedEventArgs e)
{
TextBox tb = sender as TextBox;
if (tb != null)
{
tb.GetBindingExpression(TextBox.TextProperty).UpdateSource();
}
}
ViewModel
Fields
private RelayCommand _logInCommand;
private string _password;
private string _username;
Properties
public bool CanExecuteLogInCommand
{
get
{
return !string.IsNullOrWhiteSpace(this.Username) && !string.IsNullOrWhiteSpace(this.Password);
}
}
public RelayCommand LogInCommand
{
get
{
// or you can create instance in constructor: this.LogInCommand = new RelayCommand(this.ExecuteLogInCommand, () => this.CanExecuteLogInCommand);
return this._logInCommand ?? (this._logInCommand = new RelayCommand(this.ExecuteLogInCommand, () => this.CanExecuteLogInCommand));
}
}
public string Username
{
get { return this._username; }
set
{
// a) shorter alternative -> "True if the PropertyChanged event has been raised, false otherwise"
if (this.Set(() => this.Username, ref this._username, value))
{
// raise CanExecuteLogInCommand
this.LogInCommand.RaiseCanExecuteChanged();
}
// b) longer alternative
//if (value == this._username) { return; }
//this._username = value;
//this.RaisePropertyChanged(() => this.Username);
//this.LogInCommand.RaiseCanExecuteChanged();
}
}
public string Password
{
get { return this._password; }
set
{
if (this.Set(() => this.Password, ref this._password, value))
{
this.LogInCommand.RaiseCanExecuteChanged();
}
}
}
Methods
private void ExecuteLogInCommand()
{
// .... = this.Username;
// .... = this.Password;
}
Check this sample.
To get the View and ViewModel 'linked up' so that they are synchronized, you need to implement INotifyPropertyChanged (Encapsulated in ViewModelBase). i.e.:
private string userName;
public string UserName
{
get { return userName; }
set
{
if (value != userName)
{
userName = value;
RaisePropertyChanged("UserName");
}
}
}

Categories

Resources