Prism WPF: Loading a User Control on Startup - c#

I've seen some examples on changing the user control in only one window using Prism for WPF and it looks like this:
Bootstrapper.cs
protected override void ConfigureContainer()
{
base.ConfigureContainer();
Container.RegisterType(typeof(object), typeof(ViewA), "ViewA");
Container.RegisterType(typeof(object), typeof(ViewB), "ViewB");
}
MainWindowViewModel.cs
public class MainWindowViewModel : BindableBase
{
private readonly IRegionManager _regionManager;
public DelegateCommand<string> NavigateCommand;
public MainWindowViewModel(IRegionManager regionManager)
{
_regionManager = regionManager;
NavigateCommand = new DelegateCommand<string>(ExecuteNavigateCommand);
}
private void ExecuteNavigateCommand(string uri)
{
_regionManager.RequestNavigate("ContentRegion", uri);
}
}
MainWindow.xaml
<Button Grid.Row="1" Grid.Column="1" Command="{Binding NavigateCommand}" CommandParameter="ViewA" FontSize="16" Content="View A" Margin="4"/>
<Button Grid.Row="1" Grid.Column="2" Command="{Binding NavigateCommand}" CommandParameter="ViewB" FontSize="16" Content="View B" Margin="4"/>
You can click it and the views will change. But when you start it, there is no user control loaded, only the main window. My question is How can you load a user control to the MainWindow on start of the application?

You have different options here. First of all, register the view for the region so that it is automatically discovered and displayed. That works if you don't want to navigate to that view afterwards.
_regionManager.RegisterViewWithRegion("MyRegion", typeof(ViewA));
Alternatively, you can navigate to the view when the application is done with starting. That is, from the end of Bootstrapper.InitializeModules. If the bootstrapper doesn't know of the view or you want to do other things at this moment, too, you can also publish an event like ModulesInitialized and let the module defining your view subscribe to that event.
// in the assembly defining the interfaces shared between your modules
public class ModulesInitialized : PubSubEvent {}
// in the bootstrapper.InitializeModules
Container.Resolve<IEventAggregator>().GetEvent<ModulesInitialized>().Publish();
// in the module defining viewA
_eventAggregator.GetEvent<ModulesInitialized>().Subscribe( () => _regionManager.Requestnavigate( "MyRegion", "ViewA" ), true );

Related

View inside other View (WPF/MVVM)

I have this UserControl called ControlButtonsView
<Grid>
<Button Style="{StaticResource MinimizeButton}" Command="{Binding MinimizeAppCommand}" Height="40" Width="120" VerticalAlignment="Top" HorizontalAlignment="Right"/>
<Button Content="X" Style="{StaticResource ExitButton}" Command="{Binding ExitAppCommand}" Height="40" Width="60" VerticalAlignment="Top" HorizontalAlignment="Right"/>
</Grid>
and ControlButtonsViewModel
class ControlButtonsViewModel
{
private MainWindow _mainWindow;
public ICommand MinimizeAppCommand { get; set; }
public ICommand ExitAppCommand { get; set; }
public ControlButtonsViewModel(MainWindow mainWindow)
{
_mainWindow = mainWindow;
MinimizeAppCommand = new BaseICommand(MinimizeApp);
ExitAppCommand = new BaseICommand(ExitApp);
}
public void MinimizeApp(object obj)
{
_mainWindow.WindowState = System.Windows.WindowState.Minimized;
}
public void ExitApp(object obj)
{
_mainWindow.Close();
}
}
In my MainWindow.xaml.cs
this.DataContext = new AppManagerViewModel();
AppManagerViewModel controls the switching between Views
What I want is to be able to use this ControlButtonsView with its ControlButtonsViewModel in multiple other Views, this view is a UserControl with a minimize and a maximize buttons and I want to use them in multiple Views, in LogInView, MenuView etc.
If there is an easier way to do this please tell me) Thank you.
Window logic does not belong to the view model. View model does not care about UI. You must always implement the view model pretending like there is no UI, only a model.
Therefore having a reference of MainWindow in you view model will lead to a tight coupling of the application to the view/UI.
The goal of MVVM is to remove this tight coupling. Obviously, due to the tight coupling you have introduced, you are currently not implementing the MVVM pattern (you are implementing it wrong).
For example, you won't be able to test the view model without creating a view.
Injecting the view as constructor dependency makes it even worse.
Because the commands execute UI logic (close, minimize), they have to be moved to a control - to the view component from a MVVM point of view.
To make those commands available throughout your view or globally relative to the actual visual tree, you should implement those commands as routed commands e.g. on your MainWindow, which you want to control via commanding.
Since routed commands are static, they can be referenced by every other control. Because they are routed, they can be used everywhere in the same visual tree that the command target (the MainWindow) belongs to.
Internally the command, once executed, will raise a routed event which will traverse the visual tree until it finds a handler.
Commanding Overview
In your case, MainWindow will register the Execute and CanExecute handler to close or minimize itself.
The following example implements only the logic to close the Window.
You can follow the pattern to provide additional logic e.g. to maximize the Window:
MainWindow.xaml.cs
partial class MainWindow : Window
{
public static readonly RoutedUICommand CloseWindowRoutedCommand = new RoutedUICommand(
"Closes the application.",
nameof(MainWindow.CloseWindowRoutedCommand),
typeof(MainWindow));
public MainWindow()
{
InitializeComponent();
this.CommandBindings.Add(
new CommandBinding(MainWindow.CloseWindowRoutedCommand,
ExecuteCloseWindow,
CanExecuteCloseWindow));
}
private void CanExecuteCloseWindow(object sender, CanExecuteRoutedEventArgs e) => e.CanExecute = true;
private void ExecuteCloseWindow(object sender, ExecutedRoutedEventArgs e) => Close();
}
ControlButtonsView.xaml
<Grid>
<-- ICommand traverse visual tree until handler(s) is found -->
<Button Content="X" Command="{x:static MainWindow.CloseWindowRoutedCommand}" />
</Grid>
In AppManagerViewModel, add a property of ControlButtonsViewModel.
public ControlButtonsViewModel ControlButtonsViewModel {get; set;}
In the constructor of AppManagerViewModel, add
ControlButtonsViewModel = new ControlButtonsViewModel();
In Xaml of AppManagerView,
<ControlButtonsView DataContext="{Binding ControlButtonsViewModel}" ... />

How can I set two Content region in a WPF apps using Prism 6

I have a scenario. I am writing a WPF app using Prism 6.0, where I want to POP up a Child Window first that will have three buttons for three different UI design. Similar like this.
Based upon the Selection I will update the MainWindowViewModel and will close the Child Window, and show the MainWindow.
Until This part is good. But the problem is after this part, The three different buttons points to three different UI designs. Specially the ContentRegion1 and ContentRegion2. These two regions are different.
I have seen that if I put a command through a Button then this code runs successfully. But the same doesn't run if I put that in MainWindowViewModel.
public MainWindowViewModel(IRegionManager regionManager, IEventAggregator eventAggregator)
{
_regionManager = regionManager;
_eventAggregator = eventAggregator;
_regionManager.RequestNavigate("ContentRegion1", "firstUiDesign");
...
}
The MainWindowlooks like this ...
ContentRegion1 and ContentRegion2 are two designed in XAML in this way
<Border CornerRadius="15" Grid.Column="0">
<StackPanel>
<ContentControl prism:RegionManager.RegionName="ContentRegion1" />
</StackPanel>
</Border>
<Border CornerRadius="15" Grid.Column="1">
<StackPanel Grid.Column="1" Margin="2">
<ContentControl prism:RegionManager.RegionName="ContentRegion2" />
</StackPanel>
</Border>
However I am unable to figure out what I did wrong or what Extra thing I need to put into the code to make it work.
Even in the BootStrapper.cs also I have this code
BootStrapper Code:
protected override DependencyObject CreateShell()
{
//return base.CreateShell();
return Container.Resolve<MainWindow>();
}
protected override void InitializeShell()
{
Application.Current.MainWindow.Show();
}
protected override void ConfigureContainer()
{
base.ConfigureContainer();
Container.RegisterTypeForNavigation<TestUserControl>("firstUiDesign");
}
Can anybody help in this.
Don't use the ViewModelLocator to create the MainWindowViewModel. Create it yourself in the Bootstrapper after the MainWindow and the regions have been created:
protected override DependencyObject CreateShell()
{
return Container.Resolve<MainWindow>();
}
protected override void InitializeShell()
{
var mainWindowViewModel = Container.Resolve<MainWindowViewModel>();
Application.Current.MainWindow.DataContext = mainWindowViewModel;
Application.Current.MainWindow.Show();
}
protected override void ConfigureContainer()
{
base.ConfigureContainer();
Container.RegisterTypeForNavigation<TestUserControl>("firstUiDesign");
}
Remove this from MainWindow.xaml:
prism:ViewModelLocator.AutoWireViewModel="True">

Closing a UserControl in a Grid of MainWindow

I am trying to close a usercontrol with a button click.
The usercontrol is in a grid of the mainwindow.
This is how i open it and it works.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
UsLogin _UsLogin = new UsLogin();
OpenUserControl(_UsLogin);
}
private void OpenUserControl(UsLogin _UsLogin)
{
grdVensters.Children.Add(_UsLogin);
}
}
Now in the usercontrol i have a button to confirm the login.
(no code yet this just a mockup to show in class how i want it to look)
I want this button to close this usercontrol in grdVensters so I have my main window ready.
I can't seem to access grdVensters in my usercontrol. Can i make a link?
tried: Close current UserControl
but best answer closes MainWindow what i don't want.
This looks like what i need but can't make it work in my case. Causing a UserControl to remove itself (WPF)
Thanks in advance!
You should really go for MVVM if you want to work with WPF. There are tons of resources on the web. However, I've created a small example that should lead you to the right direction. You can show/hide the login view by setting the correlating property on the ViewModel. The BooleanToVisibilityConverter converts the bool value to a Visibility value. I also added a CheckBox to demonstrate a simple example how you could change the visible state.
XAML
<StackPanel>
<StackPanel.Resources>
<BooleanToVisibilityConverter x:Key="bToV" />
</StackPanel.Resources>
<CheckBox IsChecked="{Binding Path=IsLoginVisible, UpdateSourceTrigger=PropertyChanged}" />
<Button Content="Login"
Visibility="{Binding Path=IsLoginVisible, Converter={StaticResource bToV}}" />
<!--<yournamespace:UsLogin Visibility="{Binding Path=IsLoginVisible, Converter={StaticResource bToV}}/>-->
</StackPanel>
Code Behind
public partial class MainWindow
{
public MainWindow()
{
this.InitializeComponent();
DataContext = new MainViewModel();
}
}
public class MainViewModel : ViewModelBase
{
private bool _isLoginVisible;
public bool IsLoginVisible
{
get
{
return _isLoginVisible;
}
set
{
_isLoginVisible = value;
OnPropertyChanged();
}
}
}

MVVM Navigation Parent and Child Views

I have a GeneralView that is a Parent View and when opens it opens the Parent followed by a Child. I want to implement navigation and keep the buttons in the side (like a UserPage). Lets go to the desired behavior and followed by the code I have now.
How I have implemented the ChildView don't change, stays in the HomeView aka FriendsView.
So description Login > GeneralView (that Opens Immediately in the Home) > Click in About and the childView changes to the AboutView, click in home the HomeView is showed again.
What I have:
GeneralView
<UserControl x:Class="WpfWHERE.View.GeneralView"
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:WpfWHERE.View"
xmlns:ViewModel="clr-namespace:WpfWHERE.ViewModel"
mc:Ignorable="d"
d:DesignHeight="600" d:DesignWidth="800">
<UserControl.DataContext>
<ViewModel:GeneralViewModel/>
</UserControl.DataContext>
<UserControl.Resources>
<DataTemplate DataType="{x:Type ViewModel:FriendsViewModel}">
<local:FriendsView />
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModel:AboutViewModel}">
<local:AboutView />
</DataTemplate>
</UserControl.Resources>
<DockPanel Margin="0,0,0,0">
<StackPanel Orientation="Horizontal">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MaxWidth="200"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="1" x:Name="userImage" Source="/Resources/Images/profileImage.png" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Height="161" Width="180" />
<Label Grid.Column="1" x:Name="labelName" Content="NameHere" HorizontalAlignment="Left" Margin="10.4,171,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.536,1.344" Height="26" Width="67"/>
<StackPanel Grid.Column="1" Margin="0.4,202,-1.2,0" VerticalAlignment="Top" Height="200">
<MenuItem Style="{DynamicResource MyMenuItem}" Command="{Binding DataContext.SelectViewCommand, ElementName=GeneralView}" CommandParameter="FriendsViewModel" Header="Home" x:Name="Home"/>
<MenuItem Style="{DynamicResource MyMenuItem}" Header="Overview" x:Name="Overview"/>
<MenuItem Style="{DynamicResource MyMenuItem}" Header="Settings" x:Name="Settings"/>
<MenuItem Style="{DynamicResource MyMenuItem}" Header="About" Command="{Binding DataContext.SelectViewCommand, ElementName=GeneralView}" CommandParameter="AboutViewModel" x:Name="About"/>
</StackPanel>
</Grid>
<ContentControl Content="{Binding Current_ViewModel}" Height="600" Width="600"/>
</StackPanel>
</DockPanel>
GeneralViewModel
class GeneralViewModel:AViewModel
{
public GeneralViewModel()
{
this.AddViewModel(new FriendsViewModel() { DisplayName = "Friends", InternalName = "FriendsViewModel" });
this.AddViewModel(new AboutViewModel() { DisplayName = "About", InternalName = "AboutViewModel" });
this.Current_ViewModel = this.GetViewModel("FriendsViewModel");
}
}
AViewModel Interface
public abstract class AViewModel : ViewModelBase
{
public string Name { get; set; }
public RelayCommand<string> SelectViewCommand { get; set; }
public AViewModel()
{
SelectViewCommand = new RelayCommand<string>(OnSelectViewCommand);
}
private static ObservableCollection<ViewModelBase> _ViewModels;
public static ObservableCollection<ViewModelBase> ViewModels
{
get { return _ViewModels; }
set { _ViewModels = value; }
}
public void AddViewModel(ViewModelBase viewmodel)
{
if (ViewModels == null)
ViewModels = new ObservableCollection<ViewModelBase>();
if (!ViewModels.Contains(viewmodel))
ViewModels.Add(viewmodel);
}
public ViewModelBase GetViewModel(string viewmodel)
{
return ViewModels.FirstOrDefault(item => item.InternalName == viewmodel);
}
private void OnSelectViewCommand(string obj)
{
switch (obj)
{
case "ExitCommand":
Application.Current.Shutdown();
break;
default:
this.Current_ViewModel = this.GetViewModel(obj);
break;
}
}
private ViewModelBase _Current_ViewModel;
public ViewModelBase Current_ViewModel
{
get { return _Current_ViewModel; }
set { _Current_ViewModel = value; OnPropertyChanged("Current_ViewModel"); }
}
}
Try this....
Change this...
<StackPanel Grid.Column="1" Margin="0.4,202,-1.2,0" VerticalAlignment="Top" Height="200">
<MenuItem Style="{DynamicResource MyMenuItem}" Command="{Binding DataContext.SelectViewCommand, ElementName=GeneralView}" CommandParameter="FriendsViewModel" Header="Home" x:Name="Home"/>
<MenuItem Style="{DynamicResource MyMenuItem}" Header="Overview" x:Name="Overview"/>
<MenuItem Style="{DynamicResource MyMenuItem}" Header="Settings" x:Name="Settings"/>
<MenuItem Style="{DynamicResource MyMenuItem}" Header="About" Command="{Binding DataContext.SelectViewCommand, ElementName=GeneralView}" CommandParameter="AboutViewModel" x:Name="About"/>
</StackPanel>
To this...
<StackPanel Grid.Column="1" Margin="0.4,202,-1.2,0" VerticalAlignment="Top" Height="200">
<MenuItem Style="{DynamicResource MyMenuItem}" Command="{Binding SelectViewCommand}" CommandParameter="FriendsViewModel" Header="Home" x:Name="Home"/>
<MenuItem Style="{DynamicResource MyMenuItem}" Header="Overview" x:Name="Overview"/>
<MenuItem Style="{DynamicResource MyMenuItem}" Header="Settings" x:Name="Settings"/>
<MenuItem Style="{DynamicResource MyMenuItem}" Header="About" Command="{Binding SelectViewCommand}" CommandParameter="AboutViewModel" x:Name="About"/>
</StackPanel>
Note that I have removed 'DataContext' and 'ElementName' from your MenuItems
INotifyProperty is already implemented in ViewModelBase
UPDATE 1
The problem was with ElementName=GeneralView... an Element with that Name does not exist. you could have added x:Name=”GeneralView” to the top of your Base_View XAML BUT there is no need as your ContentControl was bound to Current_ViewModel in the Base_ViewModel anyway....
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 function 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_V, That's because I want to change the view that is displayed in the Base_V'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 ManiView's ContentControl
For anyone that wants the demo code that I am talking about above, you can find it here...
http://www.mediafire.com/download/3bubiq7s6xw7i73/Navigation1.rar
Also, a little update to the code... replace the AddViewModel function in AviewModel with this.....
public void AddViewModel(ViewModelBase viewmodel)
{
if (ViewModels == null)
ViewModels = new ObservableCollection<ViewModelBase>();
var currentVNs = (from vms in ViewModels where vms.InternalName == viewmodel.InternalName select vms).FirstOrDefault();
if (currentVNs == null)
ViewModels.Add(viewmodel);
}
OK so... After some more thought.... The problem with a lot of tutorial code on the Internet is that it is quite basic. The implementation of the relationship between the Data (Model) <=> View, seems to be a matter of taste. HOWEVER I have only EVER seen it done badly. Personally speaking, most of the WPF applications I have worked on have been absolute rubbish, unmaintainable and unmanageable spaghetti code....You can clearly see that the original developer has 'Learnt' as they went along, everything is everywhere and there is no consistency.... As I normally come on to a project half way though I have no choice other than to continue with the way its been started, so I never gave it much thought, until now!!.
My own understanding is that the 'Model' defines the Data Structure, the 'View' displays that Data to the user and the ViewModel is the bit in-between that translates the Data (Model) into something that can be displayed to the user.
In the ViewModel you would simply have ObservableCollections (Lists of Data (Models)) and single instances of data (a single instance of a Model). The 'View' then Binds to the ObservableCollections (and single Model instances) to display that data (using the magic of XAML and Templating etc.)....
As for passing an object (Class) to the ViewModel, I don't think you would actually need to do that. You would simply create a property in your ViewModel that represents the Data you want to display, then retrieve the Data from a 'Source' as and when needed (typically when the View is displayed but could also be periodicity on a timer\thread or something)....
The main problem with my Demo code (download) was that the constructor of the ViewModels was NEVER being called after the application had started so there was no way of refreshing the Properties, in the ViewModel, from a Data source....
There may very well be a better way to do this BUT I have fixed that problem by introducing an Event called Initialize in the 'ViewModelBase'
public delegate void MyEventHadler();
public event MyEventHadler Initialize;
public void InitializeFunction()
{
if (Initialize != null)
Initialize.Invoke();
}
that Event can then be subscribed to in the constructor of each ViewModel
public MainV2_ViewModel()
{
this.Initialize += MainV2_ViewModel_Initialize; // our new Event
}
And the Event Stub that is called when we navigate to this ViewModel\View from somewhere else....
private void MainV2_ViewModel_Initialize()
{
// So here we are retrieving a List of All users from the WCF Service
this.AllUsers = new ObservableCollection<ServiceReference1.User>( DataAccessLevel.sr1.GetAllUsers());
// Now our AllUsers property has been updated and the View will display the new data
}
Now when you switch from one ViewModel\View to another, the Initialize Event is called\raised in AviewModel\Current_ViewModel Property Setter
private ViewModelBase _Current_ViewModel;
public ViewModelBase Current_ViewModel
{
get { return _Current_ViewModel; }
set {
_Current_ViewModel = value;
// the Constructor of the ViewModel never gets called more that once on App Start
// so we have to implement/raise our own event when changing from one View to another.
if (Current_ViewModel != null)
Current_ViewModel.InitializeFunction(); // InitializeFunction will fire the event in this ViewModel, we can now initialise the properties.
OnPropertyChanged("Current_ViewModel"); }
}
this will then fire the 'Initialize' Event in the ViewModel that is being Switched too, giving us the opportunity to refresh the Data.....
The Demo Code is now a fully functioning application (still needs work of course). It provides the following functionality...
Register a New User :: Create a new user with UserName and Password
Log a User On :: Log on existing User with UserName and Password
Recover a User (forgotten password) :: Reset the Password of a registered User (using existing UserName and new Password)
In addition, error messages are returned from the WCF Service that are then displayed in an Error View (See LogOnError_ViewModel and LogOnError_View) when something goes wrong (incorrect Password etc.)
find the Demo Code here....
http://www.mediafire.com/download/881yo6reo55tm8l/Navigation1_WCF_EF_05052016.rar
The Demo Code comes with a WCF Service and a WPF application, because it uses a WCF Service it can only be run from the Visual Studio IDE (Unless you deploy the WCF service to IIS). The WCF service uses Entity Framework and (should) create\attach a database to store User Data when you first register a new user...
Maybe you can improve on the code or get some ideas.....

Routed Tunnel Events in wpf

I have got a question to wpf community here.
I am kind of not able to understand Routing Tunnel Events. In my application, I have got a window which contains a tool bar.
Window also contains usercontrols. There are some controls in Tool bar like view which are used to Hide / unhide usercontrols (Views) like in Visual Studio.
I have custom routed tunnel event in windows control. I raise custom event when a button is clicked on toolbar (hide / unhide). I need to hide a expander in child usercontrol (which has a name like "Expander 1") when button is clicked.
Can some one tell me how can I capture the raised event in the child user control?
Thanks.
Code window :
public partial class MainWindow : Window
{
private static readonly RoutedEvent HideShowMitigationEvent;
static MainWindow()
{
HideShowMitigationEvent = EventManager.RegisterRoutedEvent("HideShowMitigation",
RoutingStrategy.Tunnel, typeof(RoutedEventHandler), typeof(MainWindow));
}
public MainWindow()
{
InitializeComponent();
}
// The Standard .Net optional event wrapper
// This is required if we want to register the event handler in XAML
public event RoutedEventHandler HideShowMitigation
{
add { AddHandler(HideShowMitigationEvent, value); }
remove { RemoveHandler(HideShowMitigationEvent, value); }
}
// Raise the event. overidden from UIElement
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
// RaiseEvent(new RoutedEventArgs(HideShowMitigationEvent, this));
}
public static ExploredRisks _rootName { get; set; }
public MainWindow(GeneralTree<string> rawTreeData, Excel.Worksheet sheet,Excel.Workbook Wb)
{
//prepares the visual tree for other views
PrepareVisualTree visualTree = new PrepareVisualTree(rawTreeData, sheet);
_rootName = visualTree.getVisualTree();
var l_vm = new MainViewModel();
l_vm.Load(_rootName);
TreeListViewMultiColumned view = new TreeListViewMultiColumned( RiskViewModel.CreateTestModel(visualTree.getVisualTree()),sheet,Wb);
base.DataContext = l_vm;
InitializeComponent();
}
private void UIPanel_Loaded(object sender, RoutedEventArgs e)
{
}
private void RibbonCheckBox_Checked(object sender, RoutedEventArgs e)
{
RaiseEvent(new RoutedEventArgs(HideShowMitigationEvent, this));
}
private void SimpleClickEventHandlingCode(object sender, RoutedEventArgs e)
{
//Expander exp = ((MainWindow)(e.OriginalSource)).RiskProperties.MitigationArea;
RoutedEventArgs args = new RoutedEventArgs();
args.RoutedEvent = HideShowMitigationEvent;
RaiseEvent(args);
}
}
}
Window Xaml:
<Window>
<Ribbon x:Name="RibbonWin" SelectedIndex="0">
<RibbonTab Header="Views" KeyTip="H">
<!-- Home group-->
<RibbonGroup x:Name="ViewsGroup" Header="Views">
<RibbonCheckBox Label="Mitigation" IsChecked="{Binding IsChecked, Mode=TwoWay}" Checked="RibbonCheckBox_Checked" PreviewMouseDown="SimpleClickEventHandlingCode"/>
<RibbonCheckBox Label="Properties" IsChecked="{Binding IsChecked, Mode=TwoWay}" Checked="RibbonCheckBox_Checked" />
</RibbonGroup>
</RibbonTab>
</Ribbon>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<UI:TreeListViewMultiColumned x:Name="RiskProperties" Grid.Column="0" />
</Grid>
</Window>
I think I have to to clarify on WPF Routed Events before I suggest a solution:
In WPF there is a new concept of Routed Events. Routed Events are Events that are passed along the logical tree.
Example:
Lets look at what happens when you click a button on your UI.
First, you will get a PreviewLeftMouseButtonDown event that occurs on the MainWindow and is then passed down the element tree from parent to child until it reaches the button that has been clicked.
-> This process (from parent to child) is called Tunneling
Second, you will get a LeftMouseButtonDown event that occurs on the button and is passed up the element tree until it reaches the MainWindow.
-> This process (from child to parent) is called Bubbling
As far as I understand you want to open the expander on the click of the button.
IMHO using routed events for this is not the appropriate approach.
I think you can solve your use case with a little XAML. Here is what I suggest:
You use a ToggleButton in the Toolbar (this ensures that the user can
see the state of the button, e.g. pressed or not pressed.)
You use DataBinding to bind the ToggleButtons IsChecked Property to the
Expanders IsExpanded property.
Check the following (highly simplified) sample:
<Grid>
<StackPanel>
<ToggleButton x:Name="openExpanderBtn" Width="100" Height="30" Margin="20" Content="Click to Open" />
<Expander Width="150" Height="200" IsExpanded="{Binding ElementName=openExpanderBtn, Path=IsChecked}" >
<Expander.Header>
This is my Header
</Expander.Header>
This is my Body
</Expander>
</StackPanel>
Remark: It just came to my mind that this only works if the UserControl is under your control. If this is the case: fine, else I will describe another solution.
Rgds MM

Categories

Resources