WPF User Control nested control - switch Usercontrol from Usercontrol - c#

I'm currently developing an application with WPF in Visual Studio as a front-end for a MySQL database which is then supposed to be used in a school to make the organisation of hardware etc. a bit more easy.
I'm totally new to C# and WPF and therefore now ran into an issue I was not able to solve in the last hours.
The UI consists of a Window with a Navbar etc. and a big Frame/Grid which is used to display the current UserControl.
Clicking onto a Button in my Mainwindow's navbar does trigger an Event which then switches the UserControl without any problems simply with these lines:
ContentFrame.Children.Clear(); //ContentFrame is a simple Grid which I am using ot display the UserControls
ContentFrame.Children.Add(new UserControlDashboard()); //UserControlDashboard is the Class of one of my UserControls
I do not know if this is really the best way to implement that (since it always reloads the UserControl), but at least it is simple and working.
The problem is, that I am only able to Switch the UserControls via the Mainwindow Class. But I want to be able to switch the UserControl from within one of the UserControls. (E.g. One of my UserControls shows a dataGrid with all the data from one of my db tables. By double clicking on one of these rows I want to be able to switch the current UserControl with that table to a different one.)
But I can't really figure out how I can do that. I've done some research but only found solutions which consisted of douzens of different classes with lots of different Eventhandlers etc. and unfortunately I couldn't really figure out how that implementation worked. And it was also limited to 2 UserControls.
Is there any way I can implement that with a reasonable amount of time? I've read that it might be possible to do by using Routed Events? Since I'm new to C# I am totally new to events, dispatchers etc. and therefore have a hard time with all that event-based stuff. :D
Thanks :)

A simple solution would be to use data binding:
MainWindow.xaml
<Window>
<StackPanel>
<SwitchingControl x:Name="BindingSourceControl" />
<ContentControl x:Name="ContentFrame"
Content="{Binding ElementName=BindingSourceControl, Path=SelectedControl}" />
</StackPanel>
</Window>
SwitchingControl.xaml.cs
partial class SwitchingControl : UserControl
{
public static readonly DependencyProperty SelectedControlProperty = DependencyProperty.Register(
"SelectedControl",
typeof(Control),
typeof(SwitchingControl),
new PropertyMetadata(default(Control)));
public Control SelectedControl
{
get => (Control) GetValue(SwitchingControl.SelectedControlProperty);
set => SetValue(SwitchingControl.SelectedControlProperty, value);
}
// Dictionary to store reusable controls
private Dictionary<string, Control> ControlMap { get; set; }
public SwitchingControl()
{
this.ControlMap = new Dictionary<string, Control>()
{
{ nameof(UserControlDashboard), new UserControlDashboard()) }
};
}
// TODO::Invoke when a DataGrid row was double clicked
private void OnNewControlSelected(string selectedControlKey)
{
if (this.ControlMap.TryGetValue(selectedControlKey, out Control selectedControl)
{
this.SelectedControl = selectedControl;
}
}
}
A more advanced solution would involve DataTemplate and different view models or data models, which specific type would map to a specific control. The control are then displayed, when a model is added e.g. to a ContentPresenter, which would automatically apply the correct DataTemplate in order to visualize the model data.

Related

How can I pass informations between the form and the user controls?

I have a problem with my project. Since I do not like it that every Windows Form is a new window I did try to use an different way.
This way I did create an FormGUI which contains the menu and calls the different UserControls. Since we did only learn how to use Windows Forms I do know how to work with them. There I would have changed the constructor of the new Windows Form to pass Data.
But this time I use mainly one Windows Form and different UserControls.
e.g. I have one to add new data, an other one to show data in a datagrid and one to show the data inside of an chart. At least this is the result.
But I do not finde a way to transport informations from the Windows Form into one User Control and how to get them back. In order to use this knowlegde somewhere else.
Does someone knew a tutorial where I can see how it could work? Or could explain it to me.
Since the UI is part of the end project that will be marked I would prefer my new way. But if I can't find a work around I will need to change it so that I would work with different windows form. >_> But really... I do not want the application to open new windows for every task. I would prefer to only show the information in one page.
In my search I did found some tutorials about UI Design with Windows Form but only the design (where a chart would be placed it is only a picture) an not how this will work with real informations.
I hope you could understand my problem...
Let's assume that the constructor of the form is something like this
private List<MyData> _data;
public MyForm(List<MyData> data)
(
...
_data = data;
)
Declare an interface
public IDataAware
{
List<MyData> Data { get; set; }
}
and let the user controls implement it. E.g.
public MyDataGridUserControl : UserControl
{
...
public List<MyData> Data {
get { return (List<MyData>)dataGridView1.DataSource; }
set { dataGridView1.DataSource = value }
}
}
If you are working with a BindingSource, access the BindingSource instead of the grid control. Now, you can access all your user controls through the same interface. In the form you can create a field
private IDataAware _dataUserControl;
In a menu item click routine, you could do some thing like this
_dataUserControl = new MyDataGridUserControl();
in another one
_dataUserControl = new MyChartUserControl();
But all the user controls implementing IDataAware you can do
Controls.Add(_dataUserControl);
_dataUserControl.Data = _data;
Note: Forms, Controls and UserControls are just classes as any other class, and you can do all the object-oriented things with them as with any c# class. (There are some restrictions with generics, however.)

Opening a second Window from MainWindow following MVVM and loose coupling

At first: This App and Question is for learning purpose
I'm on a new application and facing the problem that I want to open a Window when the user clicks on a Button in the MainView. In the past I'd have designed a Command which just creates the new Window and displays it
new RelayCommand((x)=>new SecondWindow().Show());
Now with this new Project I'm trying to fully decouple all classes from each other. To achieve this my App consists of 4 Assemblies (BL, COM, DAL and UI).
As in each WPF Application, the App starts with the MainWindow.xaml. The MainWindow.cs will create it's instance of MainWindowViewModel:
public ViewModel VM {get; private set;}
public class MainWindow(){
VM = new ViewModel();
InitializeComponent();
}
(which already violates loose coupling) (Any tips on how to make it better?)
My last attempt is to create an instance of my second Window inside my main window
<Window.Resources>
<local:SecondWindow x:Key="sw"/>
</Window.Resources>
and pass it as a CommandParameter to my Command
CommandParameter="{StaticResource sw}"/>
new RelayCommand((x)=> ((Window)x).Show());
This solution works but has one big disadvantage - the second window get's created immediately after the app starts - and so does it's ViewModel which starts some heavy processes (DB Connections etc.)
I've heard something abour IoC principle but I really don't know how to use it with an wpf application.
You are thinking along the right lines.... you basically have to create a List of ViewModels as your application starts up, then you can switch between them as the user presses buttons and pass the name of the ViewModel as a CommandParameter to your Command handler....
You might find this link to Rachel Lim's Blog
https://rachel53461.wordpress.com/2011/12/18/navigation-with-mvvm-2/
Also, I'm not going to post any code here coz it simply gets too complicated. So here is a download to just about the simplest example I could come up with
http://www.mediafire.com/download/3bubiq7s6xw7i73/Navigation1.rar
Download and un-RAR it (with win RAR) You will need to step though the code, figure out what its doing and how its doing it then modify it to suit your needs... Or modify your needs to suit the code.....
The example is a modification of Rachel Lim example. It simply contains Views and ViewModels, there are no Models or data. It demonstrates switching between two different Views.
UPDATE 1
With specific reference to the demo code.... Your VMs are added to a static collection of VMs (see AddViewModel function), each View ( the DataTemplate associates View with ViewModel) is selected when you click a button for example, by calling 'SelectViewCommand' which in turn sets Current_ViewModel to the selected ViewModel... the corrisponding ContentControl is then updated to display that currently selected View...
I know is confusing and very difficult to explain
When you press a button to 'change Views' you are actually changing the value of the property that your ContentControl is bound to, so you have to call the correct SelectViewCommand in the SAME instance of the class that your ContentControl is bound too...
In the demo you'll see that in the 'LogOn_View' I call
Command="{Binding DataContext.SelectViewCommand, ElementName=Base_V}"CommandParameter="Main_ViewModel"
Here I am calling the SelectViewCommand in the Base_ViewModel (x:Name="Base_V" in Base_View XAML), That's because I want to change the View that is displayed in the Base_View's 'ContentControl'
In Main_View I call
Command="{Binding SelectViewCommand}" CommandParameter="MainV1_ViewModel"
Here I am calling the SelectViewCommand in the Main_ViewModel, That's because I want to change the View displayed in the MainView's 'ContentControl'....
I typically create a WindowService class for managing window changes/dialogs in MVVM. Having "View" code in the ViewModel (i.e. Window.Show()) goes against MVVM principles. For example:
public class WindowService : IWindowService
{
public void ShowDialog<T>(ViewModelBase viewModel) where T : IApplicationDialog
{
IApplicationDialog dialog = (IApplicationDialog)Activator.CreateInstance(typeof(T));
dialog.Show();
}
}
And then your call from the ViewModel would look something like:
windowService.ShowDialog<SecondWindow>(new SecondWindowViewModel());
If you're using DI, you can pass a reference to the IoC container to the window service and create the window instances from that rather than using Activator.CreateInstance (i prefer the DI approach personally)

When to separate View from ViewModel?

So I'm working on a GUI and most of it I implemented with 1 window and used the code-behind for that window to handle most of the logic. The program is very GUI driven. Say there is a combo box, if you select something from the combo box, the data drastically changes and all the other GUI boxes/labels/grids change or clear ect ect.
I'm doing a lot of refactoring and I've been aware of MVVM, but I've never really seen the need for it. I understand what and why its used, but functionality its just easier to reference all the GUI components straight from the code behind I've found.
For example...
In my main window I have
<ComboBox x:Name="MyTitlesUI" ItemsSource="{Binding Titles}" SelectionChanged="MyTitlesUI_SelectionChanged">
So the ComboBox is tied to a List Titles in my MainWindowViewModel right?
Where should MyTitlesUI_SelectionChanged event go? It needs to go in the View correct? But what if the functionality of SelectionChanged has to do with data inside MainWindowViewModel?
Say you change the selection in MyTitlesUI and now the program has to look up up that Title string in a database. All of that database functionality is in DBClass which you declare in MainWindowViewModel. How do you access that functionality? Why would you have to do this:
In main window cs:
private void MyTitlesUI_SelectionChanged(object sender, EventArgs e)
{
viewModel.ConnectToDataBase((string)MyTitlesUI.SelectedItem);
}
In MainWindowViewModel.cs
private SelectedTitle;
public void ConnectToDataBase(string title)
{
SelectedTitle = title;
DBClass myDB = new DBClass(SelectedTitle);
.... //do stuff with myDB
}
That just seems kind of unnecessary no? This is just a mild mild example of course and maybe that seems pretty clean. But if you're doing really complex back and fourth between View and ViewModel, the reference to MyTitlesUI.SelectedItem in View may be needed in ViewModel for other functions to work hence the SelectedTitle private variable.
Now you have more assignments, more variables, more functions that just call other functions than just a simple MyTitlesUI.SelectedItem to deal with.
Why not bring the DBClass reference up to the View or similar?
Especially if you're doing a lot of UI manipulation that the information inside your ViewModel will be playing with. Say once I change the selection of Title, I need graph to clear. But my graph can't clear until my ViewModel has connected to the DB or something.
I'm going to have graphs or grids defined in my View that depend on dynamically created data in my ViewModel that needs to update. And I'm trying to wrap around what needs to be in View and what needs to be in ViewModel. It seems to be not proper to reference View from ViewModel, so something like MyTitlesUI.SelectedItem can't be called in ViewModel.
EDIT:
So going back to the Selected Item example, say I have a Treeview UI item. I want to bind that to a Treeview that I don't have yet. I create the data for it procedural with DB connect. So the user selects from the combobox the Title they want. Db Connect then creates, asynchronously, a TreeviewItem in some kind of data structure.
private SelectedTitle;
public void ConnectToDataBase(string title)
{
SelectedTitle = title;
DBClass myDB = new DBClass(SelectedTitle);
if(myDB.doneWorking)
{
myTreeView.ItemsSource = myDB.GetTree();
}
}
but functionality its just easier to reference all the GUI components
straight from the code behind I've found
Wrong. MVVM delivers a clean, Property-based approach that's much easier to work with than the txtPepe_TextChanged() winforms-like approach. Try to change the Text for a TextBlock buried deep inside a DataTemplate that is used as the ItemTemplate of a Virtualized ItemsControl using code behind... WPF is not winforms.
Where should MyTitlesUI_SelectionChanged event go?
Nowhere. MVVM works best with a property/DataBinding based approach, as opposed to a procedural event-based approach.
For instance, a ComboBox-based UI that "does stuff" when the user changes the selection in the ComboBox should be defined like this:
<ComboBox ItemsSource="{Binding MyCollection}"
SelectedItem="{Binding SelectedItem}"/>
ViewModel:
public class ViewModel
{
public ObservableCollection<MyItems> MyCollection {get;set;}
private void _selectedItem;
public MyItem SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
NotifyPropertyChanged();
DoStuffWhenComboIsChanged();
}
}
private void DoStuffWhenComboIsChanged()
{
//... Do stuff here
}
}
Now you have more assignments, more variables, more functions that
just call other functions than just a simple MyTitlesUI.SelectedItem
to deal with.
Wrong. What you have now is a Strongly Typed property in the ViewModel of type MyItem that you work with instead of the horrible casting stuff (MyItem)ComboBox.SelectedItem or things of that sort.
This approach has the additional advantage that your application logic is completely decoupled from the UI and thus you're free to do all sorts of crazy stuff on the UI (such as replacing the ComboBox for a 3D rotating pink elephant if you wanted to).
Why not bring the DBClass reference up to the View or similar?
Because DataBase code does NOT belong into the UI.
Especially if you're doing a lot of UI manipulation
You don't do "UI manipulation" in WPF. You do DataBinding which is a much cleaner and scalable approach.

MVVM when to create a viewmodel for a control?

Or should I only create viewmodels for the domain data being represented? While reading on MVVM, I came across this:
"The ViewModel is responsible for these tasks. The term means "Model of a View", and can be thought of as abstraction of the view, but it also provides a specialization of the Model that the View can use for data-binding. In this latter role the ViewModel contains data-transformers that convert Model types into View types, and it contains Commands the View can use to interact with the Model. "
http://blogs.msdn.com/b/johngossman/archive/2005/10/08/478683.aspx
If the viewmodel is a model of the view, then doesn't it make sense to put properties of the view in the viewmodel rather than on the code behind of the view itself?
I guess in making a custom control I just have a hard time deciding when I should just add a property to the control's code behind and when it is worthwhile to make a viewmodel for the control to represent it. Honestly I kind of feel that moving all of the control's view related properties to the viewmodel would clean up the code behind of the control leaving only the control logic.
However, if I were to change things like this, then at times when an item needs properties from the control itself I can no longer use {Binding ElementName = control, Path=property} and have to instead get the data context of the parent (because the current datacontext would be on the individual subitem of the observable collection.
Basically I was considering whether I should move properties from Class GraphViewer into a GraphViewerViewModel and then just bind to it.
Code is worth a million words so:
public class GraphViewerViewModel :DependencyObject
{
private const int DEFAULT_PEN_WIDTH = 2;
private const int DEFAULT_GRAPH_HEIGHT = 25;
public SignalDataViewModel _SignalDataViewModel
{
get;
set;
}
public PreferencesViewModel _PreferencesViewModel
{
get;
set;
}
}
Meanwhile
public class SignalDataViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
ObservableCollection<SignalViewModel> _signals;
public ObservableCollection<SignalViewModel> Signals
{
get
{
return _signals;
}
private set
{
_signals = value;
}
}
ObservableCollection<SignalViewModel> _AddedSignals;
public ObservableCollection<SignalViewModel> AddedSignals
{
get
{
return _AddedSignals;
}
private set
{
_AddedSignals = value;
}
}
it is a pain to type:
PenWidth="{Binding RelativeSource = {RelativeSource AncestorType={x:Type DaedalusGraphViewer:GraphViewer}},
Path = _GraphViewerViewModel._SignalDataViewModel._AxisDivisionUnit.GraphPenWidth, Mode=OneWay}"
and I'm wondering if it is worthwhile to make the change or whether I'm misunderstanding what a view model should be used for in mvvm.
I guess in making a custom control I just have a hard time deciding when I should just add a property to the control's code behind and when it is worthwhile to make a viewmodel for the control to represent it. Honestly I kind of feel that moving all of the control's view related properties to the viewmodel would clean up the code behind of the control leaving only the control logic.
In general, a custom control is 100% View layer code. As such, it really falls outside of MVVM entirely.
The main goal when making a custom control to be used within an application being designed with MVVM is to make sure that you design and build the custom control in a way that it is fully compatible with data binding. This will allow it to be used within your View layer of your application exactly like other controls.
As such, this pretty much guarantees that you'll have code behind, since implementing Dependency Properties really requires code behind. You also don't want to set the DataContext of a custom control within the control (since you want to inherit the data context of the user control or window using the control).
Basically I was considering whether I should move properties from Class GraphViewer into a GraphViewerViewModel and then just bind to it.
If the types are specific to your domain, then this is really typically more of a UserControl being used by your application. In that case, creating a ViewModel and just binding is likely good.
If this is, on the other hand, a true custom control that's made to be completely general purpose (ie: usable by anybody in any application), then keeping it as a "pure view" custom control typically means that you 1) won't take a dependency on any ViewModels or domain specific objects, and 2) not set the data context (which means no view model).

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.

Categories

Resources