Realtime update of DataGrid C# - c#

I'm creating a UI that visualizes som parts of a simulator. I am supposed to present some of the values in a table format, however I'm unable to get the table to update continuously when its already initialized (it initialized with the correct values, but is then static when you look at it, so you have to go to another page and then back to get the table to update)
This is the code:
<Page x:Class="SimulatorUI.RawDataPage"
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:SimulatorUI"
mc:Ignorable="d"
d:DesignHeight="950" d:DesignWidth="750"
Title="RawDataPage">
<DataGrid Name="dataTable" MinWidth="500" Margin="10 10 10 10" HorizontalContentAlignment="Center" HorizontalAlignment="Center"/>
</Page>
And the c# code looks like this
public partial class RawDataPage : Page
{
List<TankModule> tankList;
public RawDataPage(List<TankModule> list)
{
tankList = list;
InitializeComponent();
List<displayModule> data = loadTable();
dataTable.ItemsSource = data;
Task.Run(() => updateLoop());
}
public List<displayModule> loadTable()
{
List<displayModule> modules = new List<displayModule>();
foreach(TankModule tank in tankList)
{
modules.Add(new displayModule(tank));
}
return modules;
}
internal async Task updateLoop()
{
for (; ; )
{
dataTable.ItemsSource = null;
dataTable.ItemsSource = loadTable();
await Task.Delay(1000);
}
}
}

Data binding works best in these cases.
Change the tankList from List to ObservableCollection to begin with, and bind the DataGrid to tankList. Now, whenever you will add or remove a new item in tankList, the DataGrid will update to reflect the change.
The code-behind should look like this:
// Add this using to use ObservableCollection
using System.Collections.ObjectModel;
public partial class RawDataPage : Page
{
// I've renamed tankList to TankList, as it is a convention to name public properties in Pascal case
public ObservableCollection<TankModule> TankList { get; set; }
public RawDataPage(List<TankModule> list)
{
DataContext = this; // This will bind this instance as the data context of the view
tankList = new ObservableCollection<TankModule>(list);
}
}
In the View:
<Page x:Class="SimulatorUI.RawDataPage"
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:SimulatorUI"
mc:Ignorable="d"
d:DesignHeight="950" d:DesignWidth="750"
Title="RawDataPage">
<DataGrid Name="dataTable" ItemsSource="{Binding TankList}" MinWidth="500" Margin="10 10 10 10" HorizontalContentAlignment="Center" HorizontalAlignment="Center"/>
</Page>
Note: if you modify an item in TankList, the view might not get updated. In that case, the TankModule class must implement INotifyPropertyChanged.
You can also do something like this after modifying the list item to refresh the list:
ObservableCollection<TankModule> copy = TankList;
TankList = null;
TankList = copy;
Also, it is recommended to use a view model instead of doing all these in the code-behind.

Related

Converting to MVVM: Using a View Model instead of MainWindow.xaml.cs

I am trying to implement MVVM into my software.
What I want: I want a ViewModel.cs (ViewModel) file to substitute for the MainWindow.xaml.cs (MainWindow) file (which should only have InitializeComponent() inside)
What I did: I moved the data from my MainWindow to the newly created ViewModel.
What went wrong: I am having issues binding the MainWindow's XAML file to the ViewModel, with errors being
The name 'comPortList/donglesView' does not exist in the current context
I referenced the following links I considered related to my issue
How do XAML files associate with cs files?
binding property from another cs file with converter WPF
but I came up blank. Is there something I am missing? Please advise, or let me know if I am not providing enough info.
Helpful Data
Relevant MainWindow.xaml code: The bottom three lines (comPortList, btnPortOpen and donglesView) need to work off code in the ViewModel.
<Window x:Class="comPortTesterEX.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:comPortTesterEX"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<!-- -->
<Grid>
<ListBox x:Name="comPortList" SelectionMode="Single" Grid.Row="0" Grid.Column="0" />
<Button x:Name="btnPortOpen" Grid.Row="0" Grid.Column="1" Click="PortOpen_Click" Content ="Open Port"/>
<TreeView x:Name="donglesView" Grid.Row="1" Grid.RowSpan="3" Grid.Column="0" Grid.ColumnSpan="2">
ViewModel Code: the bottom three lines in 1. rely on code here, but I do not know how to link the two.
namespace comPortTesterEX
{
class ViewModel : ObservableObject
{
public ObservableCollection<Dongle> dongles;
DispatcherTimer timer;
public ViewModel()
{
timer = new DispatcherTimer();
timer.Tick += new EventHandler(checkAndUpdateComPortList);
timer.Interval = new TimeSpan(0, 0, 1);
timer.Start();
dongles = new ObservableCollection<Dongle>();
Trace.WriteLine("Started");
donglesView.ItemsSource = dongles;
}
private void checkAndUpdateComPortList(object sender, EventArgs e)
{
List<String> portNames = new List<String>();
foreach (string portName in SerialPort.GetPortNames())
{
portNames.Add(portName);
}
if (SerialPort.GetPortNames().Count() == 0)
{
portNames.Clear();
}
comPortList.ItemsSource = portNames;
}
...
private void PortOpen_Click(object sender, RoutedEventArgs e)
{
bool isComPortInList = false;
//Checks for each highlighted item (limited to one)
foreach (String name in comPortList.SelectedItems)
{
if (dongles.Count() == 0) // If there is nothing in bottom list -> CREATE ONE
{
createDongle(dongles, name, 0);
}
else //If there is already a list
{
for (int i = 0; i < dongles.Count(); i++) // Compare highlighted to EVERY ITEM IN LIST
{
// Check if it already exists in list
if (dongles[i].ComPortName == name)
{
isComPortInList = true;
} // return true if it does
}
if (isComPortInList == false)
{
//Added element is last element, not 0th
createDongle(dongles, name, dongles.Count - 1);
}
}
}
}
}
}
ObservableObject coding was copied from Rachel Lim's MVVM page, link is https://rachel53461.wordpress.com/2011/05/08/simplemvvmexample/
You cannot access donglesView.ItemsSource in a class other than MainWindow (technically you can, but you are not supposed to).
Instead of the private dongles and portNames fields, the view model should expose public readonly properties
public ObservableCollection<Dongle> Dongles { get; }
= new ObservableCollection<Dongle>();
public ObservableCollection<string> PortNames { get; }
= new ObservableCollection<string>();
to which the view bind like this:
<TreeView ItemsSource="{Binding Dongles}" ... />
<ListBox ItemsSource="{Binding PortNames}" ... />
Updates of the collection would look like this:
public void UpdatePortNames()
{
PortNames.Clear();
foreach (string portName in SerialPort.GetPortNames())
{
PortNames.Add(portName);
}
}
You also have to assign an instance of the view model class to the DataContext of the MainWindow, either in XAML
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
or in code
public MainWindow()
{
DataContext = new ViewModel();
InitializeComponent();
}
Turns out you can't totally count MainWindow.xaml.cs out of the equation, you can just minimize its impact and reduce everything to initialize component, and then bind it to something else like App.

WPF MVVM - DataGrid does not update changes back to database

I have a WPF DataGrid that is bound to a ObservableCollection that is in my NinjaList ViewModel
public ObservableCollection<NinjaVM> Ninjas { get; set; }
And the method where Ninjas is defined
public NinjaListVM()
{
using (var context = new NinjaApp_DatabaseEntities())
{
var ninjas = context.ninjas.ToList();
Ninjas = new ObservableCollection<NinjaVM>(ninjas.Select(r => new NinjaVM(r)));
}
}
The code in my View is as followed
<Window
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:NinjaApp_V2.Views"
xmlns:ViewModel="clr-namespace:NinjaApp_V2.ViewModel" xmlns:NinjaApp_V2="clr-namespace:NinjaApp_V2" x:Name="NinjaCRUDWindow" x:Class="NinjaApp_V2.Views.NinjaCRUD"
mc:Ignorable="d"
Title="NinjaCRUD" Height="300" Width="300"
DataContext="{Binding Ninjas, Source={StaticResource Locator}}" Loaded="onLoad">
<Grid Margin="0,10,3.6,0.4">
<DataGrid x:Name="DataGridNinjas" ItemsSource="{Binding Ninjas, Mode=TwoWay}" SelectedValue="{Binding Ninjas, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" AutoGenerateColumns="True">
</DataGrid>
<Button x:Name="btnNewNinja" Command="{Binding ShowNewNinja}" Margin="10,228,103.4,-0.4" Content="New Ninja"/>
</Grid>
</Window>
The view does show all the contents of the Ninja table into the Grid. Also when changes are made they persist throughout the time that the application is running. But changes are not being saved to the database. My understanding is that if you bind to a ObservableCollection like this using Mode = TwoWay that it should automatically update the changes back to the database. But clearly I am missing something, can somebody perhaps point out what I am doing wrong?
I am using MvvM Light as MvvM framework.
Yeah, the reason is that your Viewmodels haven't a direct access to the Database.
//opening the database
using (var context = new NinjaApp_DatabaseEntities())
{
//query the database and store the result in memory
var ninjas = context.ninjas.ToList();
//Viewmodel mapping
Ninjas = new ObservableCollection<NinjaVM>(ninjas.Select(r => new NinjaVM(r)));
} // in the end of the using statement the database will be "closed"
Manipulating an ninja doesn't affect the database.
I suspect you are using the entity framework.
so if you want to store the changes, you must reopen the Database, search for the specific ninja and overwrite the property and use the SaveChanges Methode.
For practicing you can do this in the setter
public class NinjaVM
{
private int _id;
private string _name;
public string Name
{
get { return _name; }
set
{
using (var context = new NinjaApp_DatabaseEntities())
{
var ninja = context.ninjas.FirstOrDefault(n => n.Id == _id ));
if(ninja == null)
return;
ninja.Name = value;
context.SaveChanges();
}
}
}

Cannot add rows to DataGrid after initialization

I am rather new to WPF (Visual Studio Express 2012), and like much of it, it's cool but it's not coming as easily as I would expect. Thanks to stackoverflow and examples and tutorials I'm picking it up, but on this I'm stymied.
I have a datagrid, I bind it to a list, and I expect that when I add something to the list, it shows up in the datagrid. That happens in the MainWindow function, but doesn't happen in my code to handle a button click (it used to work just fine when I had a ListBox, but a ListBox doesn't support checkboxes, at least not natively, so I want to convert it).
I'm wondering if a side note in this tutorial is important - it says the ItemSource refers to the original list, but the Items property is a converted ItemCollection. Stepping thru the code, I can see MyList gets the new items when I click the button, but it just doesn't show up in the UI.
Please help!
DataGridClass.cs:
namespace WpfTest
{
class DataGridClass
{
public bool CheckboxColumn { get; set; }
public string Text1Column { get; set; }
public string Text2Column { get; set; }
}
}
MainWindow.xaml.cs:
namespace WpfTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
List<DataGridClass> myList = new List<DataGridClass>();
public MainWindow()
{
InitializeComponent();
MyDataGrid.ItemsSource = myList;
// this works
myList.Add(new DataGridClass()
{
CheckboxColumn = false,
Text1Column = "Initialization",
Text2Column = "ABCD"
});
}
private void MyButton_Click(object sender, RoutedEventArgs e)
{
// this doesn't work
myList.Add(new DataGridClass()
{
CheckboxColumn = false,
Text1Column = "Button Clicked",
Text2Column = "1234"
});
}
}
}
MainWindow.xaml:
<Window x:Class="WpfTest.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">
<Grid>
<Button x:Name="MyButton" Content="Populate Chart" HorizontalAlignment="Left" Margin="75,36,0,0" VerticalAlignment="Top" Width="120" Click="MyButton_Click"/>
<DataGrid x:Name="MyDataGrid" HorizontalAlignment="Left" Margin="75,76,0,0" VerticalAlignment="Top" Height="151" Width="349"/>
</Grid>
</Window>
You'll need to use ObservableCollection instead of List
ObservableCollection<DataGridClass> myList = new ObservableCollection<DataGridClass>();
it implements INotifyCollectionChanged interface which allows UI to pick up changes made to collection

Bind SelectedValue of UserControl to ViewModel

In my solution; I have two projects: One is a WPF UserControl Library, and the other is a WPF Application.
The usercontrol is pretty straightforward; it's a label and a combo box that will show the installed printers.
In the WPF application; I want to use this usercontrol. The selected value will be stored in user settings.
The problem I'm having is that I can't seem to get the proper binding to work. What I need to happen is to be able to set the SelectedValue of the UserControl when the MainWindow loads; as well as access the SelectedValue of the UserControl when I go to save my settings.
My code is below, could someone point me in the right direction?
PrintQueue user control:
<UserControl x:Class="WpfControls.PrintQueue"
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:wpfControls="clr-namespace:WpfControls"
mc:Ignorable="d">
<UserControl.DataContext>
<wpfControls:PrintQueueViewModel/>
</UserControl.DataContext>
<Grid>
<StackPanel Orientation="Horizontal">
<Label Content="Selected Printer:"></Label>
<ComboBox ItemsSource="{Binding Path=PrintQueues, Mode=OneWay}" DisplayMemberPath="Name" SelectedValuePath="Name" Width="200" SelectedValue="{Binding Path=SelectedPrinterName, Mode=TwoWay}"></ComboBox>
</StackPanel>
</Grid>
</UserControl>
Print Queue Codebehind:
public partial class PrintQueue : UserControl
{
public static readonly DependencyProperty CurrentPrinterNameProperty =
DependencyProperty.Register("CurrentPrinterName", typeof (string), typeof (PrintQueue), new PropertyMetadata(default(string)));
public string CurrentPrinterName
{
get { return (DataContext as PrintQueueViewModel).SelectedPrinterName; }
set { (DataContext as PrintQueueViewModel).SelectedPrinterName = value; }
}
public PrintQueue()
{
InitializeComponent();
DataContext = new PrintQueueViewModel();
}
}
PrintQueue View Model:
public class PrintQueueViewModel : ViewModelBase
{
private ObservableCollection<System.Printing.PrintQueue> printQueues;
public ObservableCollection<System.Printing.PrintQueue> PrintQueues
{
get { return printQueues; }
set
{
printQueues = value;
NotifyPropertyChanged(() => PrintQueues);
}
}
private string selectedPrinterName;
public string SelectedPrinterName
{
get { return selectedPrinterName; }
set
{
selectedPrinterName = value;
NotifyPropertyChanged(() => SelectedPrinterName);
}
}
public PrintQueueViewModel()
{
PrintQueues = GetPrintQueues();
}
private static ObservableCollection<System.Printing.PrintQueue> GetPrintQueues()
{
var ps = new PrintServer();
return new ObservableCollection<System.Printing.PrintQueue>(ps.GetPrintQueues(new[]
{
EnumeratedPrintQueueTypes.Local,
EnumeratedPrintQueueTypes.Connections
}));
}
}
Main Window:
<Window x:Class="WPFApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfControls="clr-namespace:WpfControls;assembly=WpfControls" xmlns:wpfApp="clr-namespace:WPFApp"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<wpfApp:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<StackPanel>
<wpfControls:PrintQueue CurrentPrinterName="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.PrinterName, Mode=TwoWay}"></wpfControls:PrintQueue>
</StackPanel>
</Grid>
</Window>
Main Window View Model:
public class MainWindowViewModel : ViewModelBase
{
private string printerName;
public string PrinterName
{
get { return printerName; }
set
{
printerName = value;
NotifyPropertyChanged(() => PrinterName);
}
}
public MainWindowViewModel()
{
PrinterName = "Lexmark T656 PS3";
}
}
Controls in a library need to expose DependencyProperties that you can bind to in your view. Just like WPF's TextBox exposes a Text property.
Your PrintQueue control doesn't expose anything, and instead keeps all its state in a viewmodel that nothing outside can access. Your MainWindowViewModel has no way of getting at the stuff inside PrintQueueViewModel.
You need to expose SelectedPrinterName as a DependencyProperty in the code behind of your PrintQueue xaml. Then in MainWindow.xaml you can bind it to MainWindowViewModel.PrinterName.
If you want to user ViewModels all the way through instead, then MainWindowViewModel should be creating PrintQueueViewModel itself so it can access the properties within.
As per your update / comment:
Unfortunately DependencyProperties don't work like that. The getters/setters aren't even used most of the time, and they should ONLY update the property itself. You're sort of halfway between two worlds at the moment.
If I were in your position, and assuming you can change the library so PrintQueue.xaml doesn't have a hardcoded VM instance in the view, I would just create the PrintQueueViewModel yourself. That's how MVVM is supposed to work:
ViewModel:
public class MainWindowViewModel : ViewModelBase
{
public PrintQueueViewModel PrintQueue { get; private set; }
public MainWindowViewModel()
{
PrintQueue = new PrintQueueViewModel();
PrintQueue.SelectedPrinterName = "Lexmark T656 PS3";
}
}
View:
<Window x:Class="WPFApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfControls="clr-namespace:WpfControls;assembly=WpfControls" xmlns:wpfApp="clr-namespace:WPFApp"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<wpfApp:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<StackPanel>
<wpfControls:PrintQueue DataContext="{Binding PrintQueue}"/>
</StackPanel>
</Grid>
</Window>
Again though, control libraries generally don't have view models, and expose their state via dependency properties since they're designed to be used in XAML.
Component libraries may expose view models, but in that case they wouldn't hard code the view model in the view.
Did you write the library? If not, how did the author expect people to use it?
I think with this small changes everything should work
<ComboBox ItemsSource="{Binding Path=PrintQueues, Mode=OneWay}" DisplayMemberPath="Name" Width="200" SelectedItem="{Binding Path=SelectedPrinter, Mode=TwoWay}"></ComboBox>
private System.Printing.PrintQueue selectedPrinter;
public System.Printing.PrintQueue SelectedPrinter
{
get { return selectedPrinter; }
set
{
selectedPrinter = value;
NotifyPropertyChanged(() => SelectedPrinter);
}
}
Now from the main window you can modify SelectedPrinter on the viewmodel and the change should be reflected on the view
(PrintQueue.DataContext as PrintQueueViewModel).SelectedPrinter = ...
I tried your code and your bindings of the PrintQueueView to the corresponding view model work fine. Your problem is that the MainWindowViewModel does not know about the PrintQueueViewModel and thus cannot retrieve the value of the selected printer when the main window closes (I guess that is the scenario you want to implement).
The quickest solution to your problem would be to do the following steps:
In MainWindow.xaml, give PrintQueue a Name so you can access it in the code behind
In MainWindow.xaml.cs, override the OnClosing method. In it you can retrieve the view model as follows: var viewModel = (PrintQueueViewModel)PrintQueue.DataContext;. After that you can retrieve the selected value and save it or whatever.
In the MainWindow constructor after InitializeComponent, you can retrieve your saved value from a file and set it on the PrintQueueViewModel by retrieving it the same way as in the previous step.
Whole code in MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// Retrieve your selected printer here; in this case, I just set it directly
var selectedPrinter = "Lexmark T656 PS3";
var viewModel = (PrintQueueViewModel)PrintQueue.DataContext;
viewModel.SelectedPrinterName = selectedPrinter;
}
protected override void OnClosing(CancelEventArgs e)
{
var viewModel = (PrintQueueViewModel)PrintQueue.DataContext;
var selectedPrinterName = viewModel.SelectedPrinterName;
// Save the name of the selected printer here
base.OnClosing(e);
}
}
Please remember that the major point of view models is the ability to unit-test GUI logic and to disconnect GUI appearance and logic. Your view models should not be able to retrieve all the possible printers of your system but should obtain these values by e.g. Dependency Injection. I would advise you to read about SOLID programming.

Creating a custom image class in WPF following MVVM pattern

Im just starting out with MVVM and at the moment still find alot of things confusing.
So I am trying to keep things as simple as I can at the moment.
I am trying to write code for a custom image which later will be able to be placed on a canvas control by a user at runtime. I'm trying to use MVVM so that I will be able to save and reload the content on a canvas.
I have created a model class called CustomImage with the following code:
namespace StoryboardToolMvvm
{
public class CustomImage
{
public Uri imageLocation { get; set; }
public BitmapImage bitmapImage { get; set; }
}
}
I have a modelview class as follows:
namespace StoryboardToolMvvm
{
class CustomImageViewModel : ViewModelBase
{
private CustomImage _customImage;
private ObservableCollection<CustomImage> _customImages;
private ICommand _SubmitCommand;
public CustomImage CustomImage
{
get { return _customImage; }
set
{
_customImage = value;
NotifyPropertyChanged("CustomImage");
}
}
public ObservableCollection<CustomImage> CustomImages
{
get { return _customImages; }
set
{
_customImages = value;
NotifyPropertyChanged("CustomImages");
}
}
public ICommand SubmitCommand
{
get
{
if (_SubmitCommand == null)
{
_SubmitCommand = new RelayCommand(param => this.Submit(), null);
}
return _SubmitCommand;
}
}
public CustomImageViewModel()
{
CustomImage = new CustomImage();
CustomImages = new ObservableCollection<CustomImage>();
CustomImages.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(CustomImages_CollectionChanged);
}
private void CustomImages_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
NotifyPropertyChanged("CustomImages");
}
private void Submit()
{
CustomImage.imageLocation = new Uri(#"H:\My Pictures\whale.png");
CustomImage.bitmapImage = new BitmapImage(CustomImage.imageLocation);
CustomImages.Add(CustomImage);
CustomImage = new CustomImage();
}
}
}
And a view class:
<UserControl x:Class="StoryboardToolMvvm.CustomImageView"
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:viewmodel="clr-namespace:StoryboardToolMvvm"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<viewmodel:CustomImageViewModel x:Key="CustomImageViewModel"/>
</UserControl.Resources>
<Grid DataContext="{Binding Source={StaticResource CustomImageViewModel}}">
<Image Source="{Binding CustomImage.bitmapImage, Mode=TwoWay}" Width="150" Height="150" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="75,50,0,0" />
<Button Content="Submit" Command="{Binding SubmitCommand}" Width="100" Height="50" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,20" />
</Grid>
</UserControl>
I add this view to my MainWindow.xaml
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:StoryboardToolMvvm" x:Class="StoryboardToolMvvm.MainWindow"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:CustomImageView HorizontalAlignment="Left" Height="100" Margin="181,110,0,0" VerticalAlignment="Top" Width="100"/>
</Grid>
</Window>
I am very unsure as to whether I am on the right lines here with a MVVM pattern so any comments would be much appreciated. Also when Submit is pressed I would have expected my image to load but this does not happen can anyone advise as to why?
Many Thanks in advance..
As far as my understanding of MVVM and your question goes, I have one main comment about your code.
I think your CustomImage is actually both Model and ViewModel layer, and you should split it in two :
the Model, which would contain the path itself ;
the ViewModel, which contain the BitmapImage and initialize it from the Model and constructing time.
The path is the mere data used for saving, and it fits the Model, whereas the BitmapImage is how the data is shown and should be constructed in the ViewModel.
One advantage is that now, your BitmapImage gets its own NotifyPropertyChanged call at setting time, and you won't have anymore problem or a View part directly bound to the Model.
As for your CustomImageViewModel, this looks like more of a MainViewModel-ish thing. You can still use this to store the ViewModels.

Categories

Resources