Application doesn't start if I inherit MainWindow - c#

I have a RootViewModel class, and I want to access an UI element (instantialized in MainWindow) from there. For that I set the class this way:
class RootViewModel : MainWindow, INotifyPropertyChanged
But the application doesn't start. It compiles and throws no error but the Window doesn't appear. If I remove that MainWindow, I can't access my element that has been created in MainWindow.xaml. What can I do to solve this?
EDIT: Ok, I understand that I shouldn't be doing that, it's going against what it is MVVM. But is there a way to modify directly something from MainWindow? What should I try instead of this?

Consider changing RootViewModel to a UserControl. Give it a DependencyProperty called Element, of type UIElement.
Add RootViewModel to the XAML for MainWindow and bind to the element you want to use, like this;
<RootViewModel Element="{Binding ElementName=SourceElement}"/>

WPF windows are objects, so you can always instantiate them manually, like so:
var foo = new FooWindow(); // new Window object
foo.Show(); // show window as non-blocking "dialog"
If you do that, you have access to any public or protected members of the window - that includes any child controls, as long as their Accessibility properties are marked accordingly. So, if FooWindow had a TextBox named txtFooName, you could access it like so:
string name = foo.txtFooName.Text // get string value from textbox
You can also assign to any public/protected members:
foo.txtFooName.Text = "Fizz Buzz, Inc.";
Now, MainWindow is generally set as the StartupUri of the application (in App.xaml), which makes it the entry point for the application, so I'm not entirely sure what you're trying to do.

I was able to achieve what I wanted by creating a
public ObservableCollection<ChartPlotter> myPlotCollection { get; set; }
And then adding a ChartPlotter there, and setting in XAML:
<DockPanel Grid.Column="2">
<ItemsControl Width="Auto"
Height="Auto"
ItemsSource="{Binding myPlotCollection}">
</ItemsControl>
</DockPanel>
So this way I have complete control over what is happening in myPlotCollection[0]. At this moment it's enough for me, later I'll give it another try to bind it properly.

Related

How to allow WPF to resolve ContentTemplate for a Window control when using Prism?

I am trying to create a simple dialog-type control that is based on the WPF Window class (Popup won't do the trick here).
In my app I register a DataTemplate in Application.Resources:
<Application.Resources>
<DataTemplate DataType="{x:Type local:EntitySelectorViewModel}">
<local:EntitySelector></local:EntitySelector>
</DataTemplate>
</Application.Resources>
In my Window control I set Window.Content and I expect WPF will set the ContentTemplate to an instance of EntitySelector based on the DataTemplate registration shown above:
[Export(typeof(EntitySelectorDialog))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public partial class EntitySelectorDialog : Window
{
[ImportingConstructor]
public EntitySelectorDialog(EntitySelectorViewModel vm)
{
InitializeComponent();
// DataContext = vm; // does not work
// EDIT: Per two answers shown below the following should work but it does not.
Content = vm;
}
}
The problem is that WPF does not resolve the ContentTemplate i.e. an instance of EntitySelector is not created. Furthermore, when I look at the XAML for EntitySelectorDialog I see that an instance of the shell has been injected
into the Window control (EntitySelectorDialog).
I don't know enough about Prism to know if I want to go with the flow and use the shell somehow or if I want to prevent Prism from injecting it at all. I don't think I have any need for it in this specific control so if it makes sense to just prevent Prism from injecting it I would prefer that route.
For the record I have tried removing the Prism attributes from my Window control and I new up the components manually. That appears to have no effect - Prism still manages to somehow inject the shell and my ContentTemplate is not resolved.
There is no XAML to show for the Window control (EntitySelectorDialog) except the Window declaration itself - I want the content to come entirely from the ContentTemplate (EntitySelector).
I've looked at this which may provide an answer but I don't know how to implement it without breaking the rest of the app:
Getting Unity to Resolve views in XAML
Set the Content of the window to a ContentControl and set or bind the Content property of this one to the view model:
[Export(typeof(EntitySelectorDialog))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public partial class EntitySelectorDialog : Window
{
[ImportingConstructor]
public EntitySelectorDialog(EntitySelectorViewModel vm)
{
InitializeComponent();
DataContext = vm;
Content = new ContentControl() { Content = vm };
}
}
You need to set vm to EntitySelectorDialog.Content to trigger WPF solve the DataTemplate according to the type. So you either add
Content = vm;
in constructor or add
Content = {Bing}
in Xaml.

How to bind to a property of an instantiated class in XAML

I was wondering if it is possible to make a reference to an instance made in code behind of a class, with XAML.
For example:
I have two clasess, MainWindow and MainWindow_ViewModel.
MainWindow is defined like so:
public partial class MainWindow : Window
{
static public MainWindow wn;
private MainWindow_ViewModel _mwvm;
public MainWindow_ViewModel mwvm
{
get
{
return _mwnm;
}
}
public MainWindow()
{
InitializeComponent();
wn = this;
_mwvm = new MainWindow_ViewModel();
}
}
1) How could I, in MainWindow.xaml, make a reference to the property MainWindow.wn.mwvm without creating a new instance of MainWindow_ViewModel (the purpose to this is to do some binding without using DataContext but I need to use the same instance of MainWindow_ViewModel throughout the whole application)
2) Is it possible to make a reference to that same property (MainWindow.wn.mwvm), from a XAML other than MainWindow.xaml ?
Thanks again for all the support.
First of you should set the DataContext of your window to your ViewModel. Only that way will it's properties be visible in the MainWindow's XAML.
Example:
<Window DataContext="{Binding mwvm}">
<TextBlock Text="{Binding PathToYourPropertyInVM} />
</Window>
Allways make sure your viewmodels either implement INotifyPropertyChanged or use DependencyProperties for Bindable properties. That is required in order for the UI to "listen" to the changes in the properties values.
If you want a Globally accessible ViewModel, you should look into the ServiceLocator pattern. All of the MVVM framework implement it for you (MVVMLight, Caliburn, etc). It basically consist of a class where your register your components and it handles all the instatiation when it's required. You should do a little research about that topic because it's rather extensive and hard to put on a single answer.
Hope this helps ;)
To instantiate your class in the app.xaml :
Add the namespace in the app.xaml
xmlns:myApp="clr-namespace:WpfApplication1"
Create the object of your class
<myApp:MainWindow_ViewModel x:Key="mwvm" />
An example of use :
<TextBlock Text="{Binding Source={StaticResource mvvm}, Path=A_PROPERTY}"/>

Correct design for binding data to a XAML control in a view, from the ViewModel

For context, I am building a universal Windows Store app.
I'm just starting to learn C# and MVVM patterns and I need help correctly implementing binding.
I have followed this tutorial (Binding) and understand how it works, however in this example the code which does the binding is stored within the View Class.
public partial class MainPage : Page
{
public ObservableCollection<TermTest> MyTerms = new ObservableCollection<TermTest>();
public MainPage()
{
this.InitializeComponent();
MyTerms.Add(new TermTest("BNC", "Wire"));
MyTerms.Add(new TermTest("Lens", "Collects light"));
this.DataContext = new CollectionViewSource { Source = MyTerms };
}
As I understand it however this is poor design. In my implementation I will be using my Model to retrieve data which will get put into an Observable Collection. Then in my ViewModel I will want to bind the ObservableCollection to the XAML controls in which it is being used, not send the Collection to the View and then call a method in the View to populate the XAML controls.
Is that the correct way of doing this and, if so, how should it be done because I do not know how to expose the XAML controls to my ViewModel (and don't think I should be, right?).
I know I can expose the control creating a new instance of Mainpage but that is useless as I would need to bind to the current instance.
Mainpage Test = new MainPage();
Can someone please help me explain this - I have been through a lot reading and either not found the answer or not understood it!
Thanks, James
To begin, you definitely have the right idea.
What you want to do is create a ViewModel object (have it implement INotifyPropertyChanged) something like:
public class MainViewModel : INotifyPropertyChanged
{
//INPC implementation
public ObservableCollection<TermTest> MyTerms
{
//Standard INPC property stuff
}
}
Note that I used a property. You can only bind to properties, and you'll need the set method to raise the PropertyChanged event.
Then, you set the data context for the view. You can do this a number of ways, but the simplest is to do this:
public MainView() //Default Constructor
{
InitializeComponent();
DataContext = new MainViewModel();
}
Finally, bind!
<ListView ItemsSource="{Binding MyTerms}"/>
Also, if you don't want to touch the code behind of your window, you can do something like this:
<Window.Resources>
<YourNamespace:MainViewModel x:Key="MainViewModel"/>
</Window.Resources>
<Grid DataContext="{StaticResource MainViewModel}">
<ListView x:Name="TermsListView" ItemsSource="{Binding MyTerms}">
</ListView>
</Grid>
If you want understand in details this pattern I recommend you read this article:WPF MVVM step by step (Basics to Advance Level)

WPF Caliburn.Micro/mvvm Navigation

I'm building a project, and one of the biggest problems I've come across until now is navigation.
I've been looking for some time now for examples of caliburn.micro/mvvm navigation, but they all seem to be really long and I couldn't really understand much of it (beginner here!).
Some info about my project:
I want there to be an outer window/shell, with menu links/tabs that open pages according to the button clicked inside an inner part of the shell, and be able to open change the page from within a one.
I currently have: ShellViewModel.cs, MainViewModel.cs, my models, and my views.
For now, all I need to know is how to make MainViewModel load inside shellviewmodel on startup(using contentcontrol/frames...), and how to move from one page to another.
You could also just write it in points, and link me to some useful examples, and I believe I could continue from there. It'd be best to get a thorough explanation of stuff if possible.
Have a read about Conductors and Screens on the official documentation.
As a simple example, your ShellViewModel could be a Conductor of one active screen (i.e. only one screen becomes active/inactive at a time):
public class ShellViewModel : Conductor<IScreen>.Collection.OneActive
You can then set the ActiveItem of the Conductor to the view model instance that you wish to be currently active:
this.ActivateItem(myMainViewModel);
A collection Conductor type also provides an Items collection which you can populate as you instantiate new windows. Viewmodels in this Items collection may be those that are currently deactivated but not yet closed, and you can activate them by using ActivateItem as above. It also makes it very easy to create a menu of open windows by using an ItemsControl with x:Name="Items" in your ShellView.
Then, to create the ShellView, you can use a ContentControl and set its name to be the same as the ActiveItem property, and Caliburn.Micro will do the rest:
<ContentControl x:Name="ActiveItem" />
You can then respond to activation/deactivation in your MainViewModel by overriding OnActivate/OnDeactivate in that class.
In ShellView you use a content control like this:
<ShellView xmlns:cal="http://caliburnproject.org/">
<StackPanel>
<Button Content="Show other view" cal:Message.Attach="ShowOtherView" />
<ContentControl cal:View.Model="{Binding Child}" />
</StackPanel>
</ShellView>
ShellViewModel:
public class ShellViewModel : Screen
{
private object Child;
public object Child
{
get{ return child; }
set
{
if(child == value)
return;
child = value;
NotifyOfPropertyChange(() => Child);
}
}
public ShellViewModel()
{
this.Child = new MainViewModel();
}
public void ShowOtherView()
{
this.Child = new FooViewModel();
}
}
So this is a very basic example. But as you see, your ShellView provides a ContentControl, which shows the child view. This ContentControl is bound via View.Model to the Child property from your ShellViewModel.
In ShellView, I used a button to show a different view, but you can also use a menu or something like that.

XAML inline data binding doesn't work; code behind binding works

Greetings folks!
I'm running into a problem with WPF databinding that I hope you can help out with. I'm new to WPF but an expereienced developer (VB 3.0-6.0, C#).
Here's the scenario:
I have a C# project called MasterPartsData which contains a number of classes which reprsent different types of parts (capacitor, diode, etc). They inherit from a base class called clsPart.
I have another C# WPF project which contains WPF UserControls (as well as a MainWindow) to visually represent the values stored in an individual MasterPartsData (MPD) object. I've created a private field in the usercontrol to hold the object with a getter and setter.
If I create a binding explicitly in the setter for the populated object:
_capacitor = value;
Binding binding = new Binding();
binding.Source = _capacitor;
binding.Path = new PropertyPath("C0uf");
this.txtC0uf.SetBinding(TextBox.TextProperty, binding);
(with _capacitor being the private object variable and C0uf being the property name)
the value correctly displays.
However I don't wish to have to explicitly create each binding in the code behind. My preference is to create the bindings inline in XAML, perhaps with a DataContext pointing to the object.
Unfortunately every different permutation I've tried fails to work; the text box doesn't show data.
I have a couple of suspicions:
1) The binding is correct, but the text box needs to be refreshed.
2) The binding is confused between the private variable and the properties.
3) Maybe the fact that the class is defined in a different project is causing issues.
4) I'm going mad and should check myself into an asylum before someone gets hurt. :)
Any help you can provide would be most appreciated. I'm more than happy to add more information, but didn't want to clutter the question with pages and pages of source.
With respect to your suspicions:
1) I think the default binding behavior of a TextBox is TwoWay, with a LostFocus update trigger, meaning that your UI focus will have to change to another control before the binding will update, if changes are made in the UI.
If changes are made in the code you need to raise the NotifyPropertyChanged event in order for the binding system to see it.
2) This is probably not the case, but it leaves the impression that you're trying to set bindings on your UserControl properties, which is not the way data binding was designed to be used in this particular kind of use case. What you want is to bind data from non-UI classes to dependency properties on your UserControls.
3) This will never matter, as long as your UI project has a reference to your classes.
4) This is a common reaction people have when beginning to use XAML and WPF. It's like instead of being handed a box of Legos, you just got handed an injection molding machine with insufficient instructions, isn't it?
Overall, this is a situation where you might need to examine your design; elements of the "Model-View-ViewModel" pattern will come in handy. If you're unfamiliar with this, it's a development pattern in which you introduce a "ViewModel" class, perhaps you can call it MasterPartsVM which contains an implementation of INotifyPropertyChanged.
The DataContext of your UserControl would be set to this MasterPartsVM class.
A brief code example, using some generic names. Given a ViewModel class with a small backing class that looks like this:
class PartViewModel : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
public PartClass Data { get; set; }
public String SomeVMProperty
{
get { return Data.SomeProperty; }
set
{
if (Data.SomeProperty != value)
Data.SomeProperty = value;
this.PropertyChanged(this, new PropertyChangedEventArgs("SomeVMProperty"));
}
}
}
class PartClass
{
public string SomeProperty { get; set; }
}
The XAML of a basic UserControl would look like this:
<UserControl x:Class="WpfApplication1.PartUserControl"
... >
<Grid>
<TextBox Text="{Binding SomeVMProperty}" Margin="68,77,104,176" />
</Grid>
</UserControl>
To connect your data class to this UserControl, you set the UserControl's DataContext property. If you do this in code, it's a matter of having a reference to your user control and the ViewModel, and then setting the property:
MyUserControlInstance.DataContext = new PartViewModel(); // or some existing PartViewModel
That combination of code should work to produce a textbox whose Text property changes every time the SomeVMProperty property is changed.
In a basic binding scenario, if your class looks like this
public class MasterPartsData
{
private string _c0uf;
public string C0uf
{
get { return _c0uf;}
set { _c0uf = value;}
}
public MasterPartsData()
{
C0uf = "Hello World!";
}
}
your XAML would look like this
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" >
<Window.DataContext>
<local:MasterPartsData />
</Window.DataContext>
<Grid>
<TextBlock Text="{Binding Path=C0uf}" />
</Grid>
</Window>
Note, there are many different approaches to setting the DataContext, you don't necessarily just have to do it in the XAML
Also, typically your MasterDataParts class would implement INotifyPropertyChanged

Categories