MVVM architecture WPF - c#

I have a little architecture problem with MVVM in WPF. I am having View which contains option to write some code or scan QRCode with computers camera.
If user choose to not scan the code, I can bindCommand` and there is no problem.
Problem is there when user choose to scan qrcode.
When user press scan code part of screen is being collapsed and camera shows on the screen. I have to do it in View code behind, so the code I am getting in View which is not good in MVVM.
Here how View`s code look like:
private void Scan_Click(object sender, RoutedEventArgs e)
{
if (_finalVideo.IsRunning)
{
_finalVideo.Stop();
}
_finalVideo = new VideoCaptureDevice(_cameraDevices[CamerasList.SelectedIndex].MonikerString);
_finalVideo.NewFrame += (s, a) =>
{
try
{
System.Drawing.Image img = (Bitmap)a.Frame.Clone();
var ms = new MemoryStream();
img.Save(ms, ImageFormat.Bmp);
ms.Seek(0, SeekOrigin.Begin);
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = ms;
bitmapImage.EndInit();
bitmapImage.Freeze();
Dispatcher.BeginInvoke(new ThreadStart(() =>
{
CameraStream.Source = bitmapImage;
ReadQrCode(bitmapImage);
}));
}
catch
{
//exc
}
};
_finalVideo.Start();
}
How I can solve that problem with MVVM?

That's quite simple, once you get the grip of it and know the difference between a "user control" and a view.
The first statement is, that ideally the code behind should be empty for a view. This is true.
However, this do not apply for user controls. User controls can and should have code behind, because they need to work self-sustained and do not have their logic extracted into some view model class.
So what's the difference between a user control and a view? Yes, they both generally derive on UserControl, but this do not make a view a user control by default. What matters is that, a view is very specific piece of UI made for one application which is very unlikely to be reused in other application.
For example, a CustomerDetailView or CustomerDetailPage in Application A is going to be different than the same view for Application B, because Application B will likely have different requirements for a CustomerDetailView.
A user control on the other side is meant to be reusable across applications, for example DatePicker, CalendarControl or a CameraControl. This control can be used in multiple applications that may need a camera for example.
Important thing here is, that the "user control" has no knowledge of your application structure, so no viewmodels, no business/domain models etc. If you want to allow ViewModels to bind to your user control (ICommand for starting and call backs for example, or bind the resulting picture to the ViewModel), then you put dependency properties into your user control.
When you use this user control in your application, you just bind your view model to these dependency properties (DP) and you got your abstraction.
TL;DR:
Code behind in a view is bad, code behind in a user control is necessary.

Related

Looping through objects to get their properties in Windows Phone

I'm trying to develop a game in Windows Phone. I'm a beginner using c# and xaml. In the xaml I've objects with the same properties only Tag property is different. What I want to do is to loop througth object tags and change for another object (image) if I find a specific tag.
I've tried some code, like this:
foreach(Image tag in img) //but it says:
foreach statement cannot operate on variables of type 'Windows.Ui.Xaml.Controls.Image' because 'Windows.Ui.Xaml.Controls.Image' does not contain a public definition for 'GetEnumerator'...
Since as a beginner you won't likely use MVVM and just leave everything in the View, what you can do is just cycle through the LayoutRoot. If you have other Panel in the layoutroot that contain images, you want to do it recursively:
MainPage()
{
DoSomethingToImages(LayoutRoot);
}
DoSomethingToImages(Panel panel)
{
foreach(Image img in panel.Children.Where(x=> x is Image))
{
DoSomething(img);
}
var panels = panel.Children.Where(x=> x is Panel);
if (panels.Count > 0)
{
foreach(Panel p in panels)
{
DoSomethingToImages(p);
}
}
}
On the other hand, this is obviously bad practice, and you would normally bind your images to your viewmodel. The only exception might be heavily customised user controls to make things work that'd be otherwise extremely time-consuming to work around with MVVM. (Note: I think that MVVM is not always necessary for custom controls, since many times all you want is some custom graphical behaviour, like a button that takes an Image Background for it's pressed state as well, and that stuff belongs to the view, but you can usually solve that with a dependency property in the view and some XAML tweaking. Also, separating the ViewModel for a custom control makes it a bit harder to copy it to another project if you put it inside your own project - you have to find the viewmodel as well! :) ).
Like others have said, you'll need a collection to loop through.
Personally, I like dictionaries.
Here's an example I think you might be able to modify for your own purposes.
This will enable you to "loop though object tags", however as HighCore mentioned, it's probably not "the right way" to do whatever you're ultimately trying to accomplish.
Image img1 = new Image();
Image img2 = new Image();
Image img3 = new Image();
img1.Tag = "tag1";
img2.Tag = "tag2";
img3.Tag = "tag3";
Dictionary<string, Image> ImgDictionary = new Dictionary<string, Image>();
ImgDictionary.Add(img1.Tag.ToString(), img1);
ImgDictionary.Add(img2.Tag.ToString(), img2);
ImgDictionary.Add(img3.Tag.ToString(), img3);
foreach (KeyValuePair<string, Image> i in ImgDictionary)
{
// do stuff with i.Value or i.Key
}
string tmp_TagName = "tag1";
if (ImgDictionary.ContainsKey(tmp_TagName))
{
Image ReturnImage;
ImgDictionary.TryGetValue(tmp_TagName, out ReturnImage);
// do something with your ReturnImage...
}

WPF, MVVM, and effective navigation / control flow

I am currently building an application based on a real world scenario, to help me learn and understand WPF and MVVM. To that end I have read and worked through Karl Shifflett's "In The Box" VSIX, and I was able to adapt most of the concepts to the application that I am working on.
While I think MVVM is a powerful design pattern, it does (seemingly) make things that were once trivial (e.g. displaying messages, navigation, interacting with multiple window), not so trivial or straightforward. Now onto the crux of my problem / confusion.
The WPF application that I am working on is a Windows based application, and I am working from a set of basic requirements:
A basic login screen
After a successful login, close the login screen and open the actual application
Simulate a typical program workflow (opening "child" windows via button clicks, displaying modal windows, etc.)
Preform data validation / error handling
Log out
I am used to working with MDI Applications on a windows platform where interactions on a parent form cause child forms to open; I understand that MDI is not something that WPF supports and I am fine with approaching development from a different perspective. My UI would still work in a similar manner to a MDI application though: I have my application layout, and as I interact with that layout my application will respond by opening windows, displaying messages, and so on. It isn't clear to me (via MVVM) how to interact with multiple windows, or how well MVVM would scale to a large application with many windows / views.
I am not opposed to using something like Prism, but I haven't found a good article on how Prism approaches my particular problem very well. Any help, advice, feedback, or otherwise is greatly appreciated!
Have you tried looking at nRoute Framework?
A link can be found here
There are actually some good tuturials about prism
Link 1
Link 2 (Part II of Link1)
Link 3
For a more straight forrward application (not very complex and modular), you can always create a aplication, with a main window that manages child usercontrols (login window, menu window, other windows ...)
For example, create a window a contentpresenter in it, and in codebehind:
public partial class ShellWindow: Window
{
public enum PagesTypes { Login, Home }
PagesTypes currentOpenedPage;
LoginUserControl login;
HomeUserControl home;
public WindowController()
{
InitializeComponent();
login = new LoginUserControl ();
login.GoToPage += new LoginUserControl.ChangePageHandler(GoToPage);
GoToPage(PagesTypes.Login);
}
public void GoToPage(PagesTypes page)
{
switch (page)
{
case PagesTypes.Login:
//Close last opened usercontrol,
....
//open new usercontrol
login = new LoginUserControl();
contentpresenter.content = login;
break;
//other pages cases
....
}
currentOpenedPage = page;
}
}
And in for example the login usercontrol:
public partial class LoginUserControl : UserControl
{
internal delegate void ChangePageHandler(ShellWindow.PagesTypes toPage);
internal event ChangePageHandler GoToPage;
public LoginUserControl()
{...}
//Methods for login
.....
internal void LoginOK()
{
if(this.GoToPage != null)
GoToPage(ShellWindow.PagesTypes.Home);
}
}
You can build a good dynamic using this method changing usercontrols, simulating diferent windows.
Hope this gives you some ideas.
MVVMing your child windows actually can be kind of easy, especially if you decide that a tabbed interface is OK. Your outer window's view model simply has a collection of ChildWindowViewModel. You create a new tab just by creating the new view model, asking the outer window to add it to it's collection, and WPF's DataTemplate awesomeness will take care of the proper display. You'll have to do some fiddling to get tab 'close' operations working the way you want. It's kind of a pain but doable.
If you really want to do MDI, there's nothing built into WPF for it (I think Microsoft has decided that it is a bad UI pattern now?), but there may be 3rd party controls out there for it. Any good one will still mirror this solution where their MDI container control will bind to your list of child window view models.

Set Background for App.RootFrame from App.Resources in wp8

Friends,
I am assigning the Background of RootFrame to application resources, It works when you explicitly write the Resource name like below
App.RootFrame.Background = (System.Windows.Media.ImageBrush)App.Current.Resources["Theme_6"];
but If I use below it doesn't work:
string themeName = "Theme_6";
App.RootFrame.Background = (System.Windows.Media.ImageBrush)App.Current.Resources[themeName];
Is it possible to use the 2nd options in wp8?
Thanks!
Panorama control (and I think Pivot controls as well) has some problem supporting late binding for background images.
When you hard-code the image paths, there is no problem displaying a static background image.
To assign background images 'on the fly' you should follow these steps;
Create a Property (MainBackGroundImage) within your associated
ViewModel, that implements INotifyPropertyChanged interface (if you
are using MVVM pattern you already have this infrastructure).
Assign any image path (this can be a remote URL as well) to this
property whenever you want to change the background image.
In your View hook up to your ViewModel's property changed event and
update the layout of the control where background image is going to
appear:
void viewModel_PropertyChanged(object sender,PropertyChangedEventArgs e)
{
if (e.PropertyName == “MainBackGroundImage”)
{
this.MainPanorama.UpdateLayout();
}
}
You might perhaps take a look at my open-source WP8 application where I did achieve dynamic background images.

WPF ScatterView binding to multiple sources

I am using a ScatterView and am currently binding to a folder so that when my app starts up some sample images are displayed, this works great.
<s:ScatterView x:Name="MainScatterView">
<s:ScatterView.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}"/>
</DataTemplate>
</s:ScatterView.ItemTemplate>
</s:ScatterView>
I then set the binding using
scatter.ItemsSource =
System.IO.Directory.GetFiles(imagesPath, "*.jpg");
This works great but then when I try add further images:
Image img = new Image();
img.Source =
new BitmapImage(new Uri("\\Resources\\Koala.jpg", UriKind.Relative));
scatter.Items.Add(img);
I get an InvalidOperationException: Operation not valid when ItemSource is in use.
What is the best way to handle this. Remove the binding and add the images manually on startup? I'm assuming then since the ItemSource is the same any further additions wont cause any problems? Or is there a better way to handle this since the binding method works really well.
cheers
This calls for a ViewModel
This type of problem, binding working well for the simple case but starting to fall down as you add scenarios, is a great indicator that it's time to use Model - View - ViewModel.
Roughly speaking, the idea is that you have a View (your XAML) and a Model (your data, in this case a set of files). But instead of directly binding the View to the Data, you add an intermediate class called the ViewModel. Your View binds to the ViewModel and your ViewModel loads itself from the Model. This gives you wiggle room to do more than simple things when loading data to be bound.
What does that mean here? It would look like:
public class MainViewModel
{
// ObservableCollection adds databinding goodness so when you add a new file,
// the UI automatically refreshes
public ObservableCollection<string> Images { get; private set; }
public MainViewModel(string path)
{
Images = new ObservableCollection<string>();
Images.AddRange(Directory.GetFiles(path, "*.jpg"));
}
public void AddImage(string path)
{
Images.Add(path);
}
}
and now in your xaml, you set your datacontext to new MainViewModel. You can do this in code behind or using a StaticResource, if you use a StaticResource you need a ctor that takes no parameters so you'll have to set your initial directory in some other way. Your binding then looks like:
<Image Source={Binding Images} />
Take a good look at the M-V-VM pattern. You'll find that it makes databinding problems like this easier and also has a host of other benefits like fewer event handlers (so fewer reference leaks), better testability, easier to work with Blend, and easier to add new types of UI technologies.
I'm also new to Surface development, anyway what I have is remove the databinding and add the images manually via a for loop.

Seeking guidance on WPF Prism Multi display application

I'm in the beginning stages of designing an application using Prism and have a question. In all the reference material I've been able to find, there is lots of details on creating a single screen application, but I have a requirements beyond that.
I would like the have two windows showing (Multi screen), both with the exact same layout but each looking at a difference source of information for their data. In other words, I have datasource A and datasource B that update very frequently and I need to monitor both of them at the same time.
Is there a way to launch a prism app multiscreen in this manner or would it better to launch separate processes for each source?
Thanks.
This should be pretty simple. Launching a new Window for each ought to do what you need (the user would have to move the window to the second monitor... I suppose you could investigate some p/invoke magic to move the window to the proper monitor if you wanted).
Do you need something more complicated?
If it's the same view with different data, I'd use MVVM and spin them off sort of like this:
MyFirstViewModel vm1 = new MyFirstViewModel();
MySecondViewModel vm2 = new MySecondViewModel();
MyView view1 = new MyView();
view1.DataContext = vm1;
MyView view2 = new MyView(vm2);
view2.DataContext = vm2;
view1.Show();
view2.Show();
Hopefully your view models can be reusable too so you wouldn't need to write a class for each, but hopefully this illustrates the strategy a little.

Categories

Resources