Access a ViewModel from another ViewModel - c#

I'm developing an WPF using MVVM pattern, C# and .NET Framework 4.6.1.
I have a Window that contains an UserControl (Control1) and that UserControl contains another UserControl (Control2). I have chosen this way to do it instead of using a Dialog Window (Control2 acts as Dialog Window).
Both user controls have a Viewmodel (Control1VM and Control2VM).
I use Control2 as a form to let users input some data that I need to start the application.
This is the MainWindow with Control1:
And this is Control2 over Control1.
My problem is that I don't know how to hide Control2 when I click on OK or Cancel button.
This is how Control2 is set on Control1:
<Grid x:Name="gridControl2" Margin="30" Grid.RowSpan="6" Grid.ColumnSpan="3" Visibility="{Binding GridControl2Visibility}">
<local:Control2 x:Name="userControlControl2" />
</Grid>
To show Control2 and set GridControl2Visibility to Visible in Control1VM:
public Visibility GridControl2Visibility
{
get { return gridControl2Visibility; }
set
{
if (gridControl2Visibility != value)
{
gridControl2Visibility = value;
RaisePropertyChangedEvent("GridControl2Visibility");
}
}
}
How can I hide Control2 when I click on Ok or Cancel button in Control2? My problem is that GridControl2Visibility is on Control1VM and I can't access that class from Control2VM.

Use a service that both view models can access and that stores the info whether Control2 should be visible or not. Ideally, the service would be registered as singleton with your di-container and injected into the view models.
Alternatively, you can use an event aggregator, which is basically a singleton service, too, but focused on distributing events rather than holding a state.

You can use events, You can raise event from Control2VM and hadnle it in Control1VM and set GridControl2Visibility to false.

Related

Property binding to a Child UserControl

I've got a problem I couldn't get solved until now:
I am developing an application in C#/WPF and am using the Caliburn.micro as framework. I have multiple menu panels (as user controls) that I want to reuse all over the application (e.g. data filtering menu for a grid) and show in a <ContentControl />. Depending on the state of the application a different menu panel can be shown.
Now I could get managed to let events bubble up from the menu's View to the parent's ViewModel. But I'm stuck with properties:
For example in the filtering menu, one should enter a text while the filter is instantly applied. When I had the menu in the parent's View it was easy: I just made the filtering in the property's setter method.
Is there a possibility to make a kind of "property-bubbling" similar to the message bubbling in c.m (it has to be twoWay!)? Or any other (better) MVVM-compliant approach?
Thanks in advance!
Jan
Minimal example:
ParentView.xaml
<UserControl x:Class="App.ParentView">
<Grid>
<ContentControl x:Name="Toolbar" />
</Grid>
</UserControl>
ParentViewModel.cs
class ParentViewModel : Screen
{
public ParentViewModel()
{
Toolbar = new MenuViewModel();
}
private Screen _toolbar;
public Screen Toolbar
{
// get, set ...
}
public void LoadDifferentMenu()
{
this.Toolbar = new DifferentMenuViewModel();
}
}
MenuView.xaml
<UserControl x:Class="App.MenuView">
<Grid>
<TextBox x:Name="MyText" />
</Grid>
</UserControl>
MenuViewModel.cs
class MenuViewModel : Screen
{
public MenuViewModel()
{
}
private string _myText;
public string MyText
{
// get, set...
}
}
Use Event Aggregator in caliburn micro to implement publisher and subscriber pattern in MVVM.
Communication is based on message type so it can be used for one way or two way communication with appropriate types.
Kindly refer to the link https://caliburnmicro.com/documentation/event-aggregator for implementation details.

Attach to a click event in XAML control when loading XAML dynamically.

I have a XAML control which gets loaded dynamically at runtime. This is pure XAML with no code behind.
I dont have any control over the parent loading mechanism which is why this looks a bit weird.
I have a parent application which loads my Plugin DLL and loads my XAML Control.
My DLL signature is:
public class Application : BaseClassHere
{
public Application(IParentContext context) : base(context)
{
// Im placing this instance in the bag which i use in my XAML
base.MyObservablePropertyBag["MyParentContext"] = new ObservableValue<object>(this);
}
}
My XAML is like this:
<av:UserControl
xmlns:local="clr-namespace:MyApplicationNS;assembly=MyDll"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:av="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" >
<Border DataContext="{Binding MyObservablePropertyBag[MyParentContext].Value}" >
<Button Name="MyButton" />
</Border>
</UserControl>
This binding works perfectly in the XAML. I have full access to all public properties that i definein the Application class.
My problem is that i want to link to up Click events on my Buttons. If i add a Click event in the XAML it errors at runtime telling me that i need to compile my XAML.
Is there any way to subscribe to the Click event on MyButton in the Application class?
Well, if you say binding works then why try to mess with the Click event? Rather go with the Button's Command property. Create an ICommand instance (like a DelegateCommand or RelayCommand) that you place in your propertybag and just bind to it!

Open referenced control in a new window

Issue
I want to load a referenced control from the main window into a new window. The referenced control is already child of the main window, causing the following exception when attempting to render the new window:
System.ArgumentException was unhandled:
Must disconnect specified child from current parent Visual before attaching to new parent Visual.
I do not want to disconnect it from the main window and I also cannot create a new instance of the control since I do not know how it's instantiated or what members are applied.
Background
I'm developing an application that allows developers to extend the application with additional views of configuration options. The container of these views may turn out to be too small for large view extensions (imagine a scheduling control for agendas as example), so I wish to provide the user with the ability to open the extended view in a new window.
Code
So far I've created a behavior to attach to Hyperlinks that opens a new window with the referenced control upon the Click event. The following code is the most basic implementation to demonstrate my intention:
public class ExpandViewBehavior : Behavior<Hyperlink>
{
public static DependencyProperty ViewProperty = DependencyProperty.Register("View", typeof(object), typeof(ExpandViewBehavior));
public object View
{
get { return GetValue(ViewProperty); }
set { SetValue(ViewProperty, value); }
}
protected override void OnAttached()
{
this.AssociatedObject.Click += AssociatedObject_Click;
}
void AssociatedObject_Click(object sender, RoutedEventArgs e)
{
if (View != null)
{
var window = new Window()
{
Content = View
};
window.Show();
}
}
}
Attached to a Hyperlink in the main window, referencing a simple TextBox to load in the new window. Where i is the System.Windows.Interactivity namespace and local my project namespace.
xmlns:local="clr-namespace:WpfApplication"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
...
<StackPanel>
<TextBlock>
<Hyperlink>
<i:Interaction.Behaviors>
<local:ExpandViewBehavior
View="{Binding Source={x:Reference SomeControl}}" />
</i:Interaction.Behaviors>
<TextBlock
Text="(Open in new window)" />
</Hyperlink>
</TextBlock>
<TextBox
x:Name="SomeControl" />
</StackPanel>
My question is, is there is a way to load the referenced control without disconnecting it from the main window?
As you can't display the same control in two places at once, you will either need to disconnect the control from the main window (as noted in the error text), or create a copy of the control to place into the child window.
You can clone the control by exporting its XAML, and then create a new control from this XAML.
See this answer How can you clone a WPF object? for more details.

Modal Popup and views communication under MVVM pattern on C#

I have a large project coded with VB6 which I've been trying to upgrade to new technologies for a few months. My project consist on 6 administrative modules reunited under a client-server application. Coming from VB, my logical choice was to upgrade to .NET. After a lot of research I decide to use C#, WPF and the MVVM pattern (with Caliburn Micro).
At the beggining I had some problems, but I managed to resolve them. But now I've come to a point where I need (like every complex application) to communicate with different views and their corresponding viewModel through modal popups (or some other technique). And in this matter the MVVM pattern seems to be very restrictive or complex. A simple "Are you sure you want to delete this record (yes/no)" is a very complex task. So I'm looking for advice as how communicate views without complex artifacts as EventAgregators.
So far the only possible alternative I've found is to use the ModalContentPresenter class from this blog. The problems with this solution are:
I need to write the father view XAML and modal XAML on the same view.
I cannot have multiple popus from same view.
Some examples of where I'd like to use modal popups is:
Put a button on a view to select a Client. It should open a popup with all posible clients and let the user chose one.
Add a product popup to a customer order.
Any ideas or suggestions? Some sample code would be appreciated? Thanks!
I am the author of the linked ModalContentPresenter control so I will attempt to address some of your questions and concerns.
I need to write the father view XAML and modal XAML on the same view.
You can actually write both views in separate files. The views can then be loaded dynamically using DataTemplates which will depend on the ViewModel that is bound to either the Content or ModalContent properties.
See this which describes the general way in which this view switching can be achieved.
You could have a MainViewModel which has two properties, PrimaryViewModel and SecondaryViewModel which return appropriate view models which provide the properties and commands for the main and modal content.
You could have the following setup in XAML:
<DataTemplate DataType="{x:Type FooViewModel}">
<Controls:FooView />
</DataTemplate>
<DataTemplate DataType="{x:Type BarViewModel}">
<Controls:BarView />
</DataTemplate>
<controls:ModalContentPresenter
Name="modalPresenter"
Content={Binding DataContext.PrimaryViewModel}
ModalContent={Binding DataContext.SecondaryViewModel} />
When the IsModalproperty is false, only your PrimaryView will be displayed. As soon as you set the IsModal property to true the ModalContentPresenter will display your SecondaryView.
I cannot have multiple popus from same view.
I take it you mean you want to be able to display different modal content at different times from the same main view.
Using the above technique this is as simple as switching the ViewModel that is bound to the ModalContent property (before displaying it by setting IsModal to true). As long as you have a DataTemplate for the ViewModel that is bound (and your MainViewModel implements INotifyPropertyChanged correctly), the correct content will be displayed.
Some example on where i'd like to use modal popups is:
Put a button on a view to select a Client. It should open a popup with
all possible clients and let the user chose one.
Add a product popup to a customer order.
Once you understand the technique described above you should be able to see that the as long as you have a View and ViewModel pair you can cover any scenario you can think of.
As an example, consider viewModels that have the following interfaces:
public interface SelectCustomerViewModel : INotifyPropertyChanged {
event EventHandler CustomerSelected;
public ObservableCollection<Customer> Customers { get; }
public Customer Customer { get; set; }
public Command CustomerSelectedCommand { get; }
}
public interface MainViewModel : INotifyPropertyChanged {
public SelectCustomerViewModel ModalContent { get; }
public Command SelectCustomerCommand { get; }
public bool IsSelectingCustomer { get; }
}
You could have XAML that looks something like this:
<Window x:Class="ModalContentTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Select a customer">
<DataContext>
<vm:MainViewModel />
</DataContext>
<DataTemplate DataType="{x:Type SelectCustomerViewModel}">
<Controls:SelectCustomerView />
</DataTemplate>
<c:ModalContentPresenter Name="modalPresenter"
ModalContent={Binding ModalContent}
IsModal={Binding IsSelectingCustomer}>
<!-- This is the primary content! -->
<Grid>
<Button Content="Select a customer"
Command={Binding SelectCustomerCommand} />
</Grid>
</c:ModalContentPresenter>
</Window>
Here's how it works:
The IsSelectingCustomer property of the MainViewModel would start off as false.
Clicking the button in the main view would invoke the SelectCustomerCommand object. The command would then tell the MainViewModel to change the IsSelectingCustomer property to true.
The ModalContentPresenter would display the view specified by the data template. The user can now only interact with the 'select customer view'.
Once a customer has been selected, a button can be clicked (which is bound to the CustomerSelectedCommand of the SelectCustomerViewModel) which in turn would raise the CustomerSelected event.
The MainViewModel would have an event handler that would respond to the CustomerSelected event. The handler would then read the SelectedCustomer property from the SelectCustomerViewModel and finally, it would set the IsSelectingCustomer property back to false, causing the modal content to be closed.

How to remove a control from window programmatically?

I have a window with a button in it, and I need to remove it OR not depending on the argument passed to the window:
public MainWindow(bool removeControl)
{
InitializeComponent();
if (removeControl)
{
//code to remove the button
}
}
In the XAML file I declare a normal button:
<Button Width="120" Height="25" Content="Click" Name="ClickButton"></Button>
I know this can be done by doing the reverse thing which means add the button depending of the Boolean parameter, but I need to do so.
You can do:
mybutton.Visibility = Visibility.Collapsed;
...or if you really want it to be removed from the "logical tree"...then it all depends what "container"/parent that Button is in, in how you remove it.
Disconnecting an element from any/unspecified parent container in WPF
Remove Control from Window in WPF
http://joe-bq-wang.iteye.com/blog/1613370

Categories

Resources