Get a MenuItem to switch to a different visible window when clicked - c#

I am working with C# in MS Visual Studio Community 2013. I am trying to set up a MenuItem so that when clicked it causes the current window to hide itself and another window appears at the same location.
I have a couple of books on the subject and I've also spent lots of time searching for answers online. However, I simply can't find out how to set this up when it's something that should be relatively straightforward to do.
I've done it before with older versions of C#, but it just seems impossible to sort out with Visual Studio Community 2013.
At this stage I have a menu and menu items set up something like the 'much simplified' following:
<Menu HorizontalAlignment="Left" Height="21" Margin="10,10,0,0" VerticalAlignment="Top" Width="1264" Grid.ColumnSpan="2" FontWeight="Medium" FontSize="14">
<MenuItem Header="Item Type" Margin="0" Width="100" Height="21" FontSize="11" Click="MenuItem_Click">
<MenuItem Header="Item Action" HorizontalAlignment="Left" Width="185"/>
</MenuItem>
</Menu>
Most, if not all of that was generated automatically, as I set up the menu in Visual Studio.
Also, if I right click on the 'Item Action' menu item, a popup appears with a 'view code' option. If I click that option, the following empty function appears in the corresponding *.cs file:
private void MenuItem_Click(object sender, RoutedEventArgs e) { }
I realise that I probably need to add code to that empty method to do what I need to do, but so far I haven't found out how to do that.
There is also a 'Command' property in the properties list for the MenuItem. I also assume that that may play a part in sorting this out, but at this stage I don't know how.
Please let me know how this needs to be set up and big thanks in advance for any help.
Also, after searching elsewhere on stackoverflow, I found the basics for hiding/showing different windows. I've got that working OK, but it's not enough to resolve my problem.
Basically, in my real menu I have multiple menuItems and the clicking of each one needs to be dealt with in a different way. Therefore, in the MenuItem_Click function I need a way of identifying which MenuItem was actually clicked, so that I can respond in the correct way for each and every MenuItem click.

You can add click event for each menu item and add code for each method.inside this method you can add this code
Window1.Visibility = Visibility.Hidden;
window2.Visibility = Visibility.Visible;
you can use Command property when you need to bind methods in MVVM Pattern

Related

Two-way binding with nested ViewModels

Using Catel 3.9 and DevExpress 15.x
My Customer has asked that I make some UI changes to an app and I'm not sure it can easily be done.
Architecture:
There is a MainWindow with an associated View and ViewModel.
The MainWindow holds a TabControl where each Tab's content is a separate View/ViewModel. The MainWindow ViewModel does NOT own any of the nested VMs; they are constructed automatically by Catel at runtime by the View.
The old UI had buttons on each TabItem which allowed the Customer to load, save, display, filter, etc. The commands/properties for these buttons were bound directly to the ViewModel for that tab and was working fine.
The Customer would rather have a single top level (on the MainWindow) menu and selections from that menu would affect whichever tab had the current focus.
I can pass commands (using Messaging or Catel's InterestedIn attribute) to the correct ViewModel, but I'd like to have a more direct binding with the top level menu and the appropriate ViewModel so I can enable/disable menu items or even modify the text to suit whichever tab is open.
I'm looking for a primarily XAML and/or Catel solution. If you need additional information, please let me know.
Suggestions?
thanks,
randy
Edit: Sorry that I didn't include additional research. If you knew me, you'd know I will spend hours/days looking for solutions to problems and only when I'm stumped will I ask for assistance. My bad for not including more.
The hardest part about this issue is defining good search parameters. Most of the suggestions were similar to: Just put everything into the MainWindow ViewModel which (to me) is not a good design choice because what is displayed on the tabs IS different and should be separate.
Other solutions were to have the MainWindow ViewModel construct each of the inner ViewModels and then manage them. With the Catel framework I'm using, the framework automatically constructs the VM when the View is loaded, injecting any required parameters to the constructor. See below -- you just reference the View and Catel will match it up with its ViewModel and create it for you. Unfortunately without taking other steps, you really don't have a reference to the VM that was created.
MainWindow.xaml:
<dx:DXTabControl x:Name="MainTabControl"
Grid.Row="1"
Margin="10"
BorderThickness="0"
SelectedIndex="{Binding SelectedTabIndex}"
>
<dx:DXTabItem Header="Getting Started" IsEnabled="True">
<views:GetStarted />
</dx:DXTabItem>
<dx:DXTabItem Header="Validate Student Records" Background="Ivory">
<views:StudentValidation />
</dx:DXTabItem>
<dx:DXTabItem Header="Validate Teacher Records" Background="Ivory">
<views:TeacherValidation />
</dx:DXTabItem>
<dx:DXTabItem Header="Validate Admin Records" Background="Ivory">
<views:AdminRecordValidation />
</dx:DXTabItem>
</dx:DXTabControl>
Examples of some possible solutions I'm looking at:
One ViewModel Member of Another
Get a property of a tabitem's ViewModel when it's selected
Edit #2: There was a suggestion about using a Service and SO doesn't allow you to add detailed comments (and it puts a TIMER on your comments -- SHEEESH!), so I'll put my response here.
Consider this scenario using a Service: The Customer starts the app and clicks on a Tab (StudentValidation). The MainWindowViewModel (via a Property) detects the selected tab and calls the Service with an update (I'm not sure what is updated; possibly some sort of State). The "nested" ViewModels are notified (via an Event) of the change in the Service. I'll assume the StudentValidationViewModel is the only one who actually responds to the event and interacts with the Service, retrieving "data".
So, now we have the StudentValidation tab displayed and the Customer goes to the Main Menu of the app. The Main Menu is STILL tied to the MainWindow and every command is bound to the MainWindowViewModel. How does the Service bind the Main Menu to the ViewModel of the currently selected tab so that the commands will be handled by the StudentValidationViewModel? I'm probably missing something.
Use a singleton model which holds the shared data so you get the instance from wherever you like.
Services are the solution. Create a solution that is injected into all view models. Then the top-level vm can update the service, and all vm's can respond to the update via events.
Remember that vm's just represent a live view in memory so you can interact with them.
Thank you for all the suggestions. I tried to upvote each one, but as a "newbie" I can't. As I worked thru each one, I realized that all I wanted to do was bind a specific subset of the Main Menu items to the View/ViewModel of the currently focused tab on the Main Window. It seemed as simple as changing the DataContext of the menu item.
Here's the Main Menu. The FileSubMenu is the one I need to bind to the currently focused ViewModel. The other menu items can be handled by the MainWindowViewModel.
MainWindow.xaml:
<dxb:MainMenuControl Grid.Row="0"
HorizontalAlignment="Left"
HorizontalContentAlignment="Stretch"
VerticalAlignment="Top"
BarItemDisplayMode="ContentAndGlyph"
>
<dxb:BarStaticItem Content="Validator" Glyph="pack://application:,,,/LexValidator;component/Images/lex-logo.png" />
<dxb:BarSubItem x:Name="FileSubMenu" Content="File">
<dxb:BarButtonItem Content="{Binding LoadRecordsText}" Glyph="{dx:DXImage Image=LoadFrom_16x16.png}" Command="{Binding LoadRecordsFile}"/>
<dxb:BarButtonItem Content="Clear Display" Glyph="{dx:DXImageOffice2013 Image=Clear_16x16.png}" Command="{Binding ClearDisplay}"/>
<dxb:BarButtonItem Content="FTE Counts..." Glyph="{dx:DXImage Image=TextBox_16x16.png}" Command="{Binding ShowFTECounts}"/>
<dxb:BarButtonItem Content="Show Pay Grid..." Glyph="{dx:DXImage Image=Financial_16x16.png}" Command="{Binding ShowPayGrid}"/>
<dxb:BarItemLinkSeparator />
<dxb:BarCheckItem Content="Show Ignored Issues" Glyph="{dx:DXImage Image=ClearFilter_16x16.png}" IsChecked="{Binding ShowIgnoredIssues}" IsEnabled="{Binding IsShowIgnoredIssuesEnabled}" />
</dxb:BarSubItem>
<dxb:BarSubItem Content="Exit">
<dxb:BarButtonItem Content="Exit" Glyph="{dx:DXImage Image=Close_16x16.png}" Command="{Binding ExitApplication}"/>
</dxb:BarSubItem>
<dxb:BarSubItem Content="Help">
<dxb:BarButtonItem Content="About..." Glyph="{dx:DXImageGrayscale Image=Index_16x16.png}" Command="{Binding ShowAboutBox}"/>
</dxb:BarSubItem>
</dxb:MainMenuControl>
Then on the TabControl, I handle the event when a new tab is selected:
MainWindow.xaml.cs
private void MainTabControl_SelectionChanged(object sender, TabControlSelectionChangedEventArgs e)
{
// Turn it off and see if it needs to be enabled.
this.FileSubMenu.IsEnabled = false;
var newTabItem = e.NewSelectedItem as DXTabItem;
if (newTabItem != null)
{
var tabView = newTabItem.Content as UserControl;
if (tabView != null)
{
var tabViewModel = tabView.ViewModel;
if (tabViewModel != null)
{
this.FileSubMenu.DataContext = tabViewModel;
this.FileSubMenu.IsEnabled = true;
}
}
}
}
I realize this may not be very "MVVM", but it works well and the "Boss" said "Move on to something else". I would be happier if the above code could be handled totally in XAML -- some sort of resource maybe?
Again, if there is something I missed or a better (more MVVM) solution, please let me know.

WPF: Which solution? TabControl with close button and new tab button

I'm trying to find the best solution for a TabControl that both support a close button on each TabItem, and always show a "new tab button" as the last tab.
I've found some half working solutions, but i think that was for MVVM, that I'm not using. Enough to try to understand WPF =)
This is the best solution I've found so far:
http://www.codeproject.com/Articles/493538/Add-Remove-Tabs-Dynamically-in-WPF
A solution that i actually understand. But the problem is that it is using the ItemsSource, and i don't want that. I want to bind the ItemsSource to my own collection without having to have special things in that collection to handle the new tab button.
I've been search for days now but cant find a good solution.
And I'm really new to WPF, otherwise i could probably have adapted the half done solutions I've found, or make them complete. But unfortunately that is way out of my league for now.
Any help appreciated.
I have an open source library which supports MVVM and allows extra content, such as a button to be added into the tab strip. It is sports Chrome style tabs which can tear off.
http://dragablz.net
This is bit of a dirty way to achieve the Add (+) button placed next to the last TabItem without much work. You already know how to place a Delete button next to the TabItem caption so I've not included that logic here.
Basically the logic in this solution is
To bind ItemsSource property to your own collection as well as
the Add TabItem using a CompositeCollection.
Disable selection of
the Add(+) TabItem and instead perform an action to load a new tab when it
is clicked/selected.
XAML bit
<TextBlock x:Name="HiddenItemWithDataContext" Visibility="Collapsed" />
<TabControl x:Name="Tab1" SelectionChanged="Tab1_SelectionChanged" >
<TabControl.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{Binding DataContext.MyList, Source={x:Reference HiddenItemWithDataContext}}" />
<TabItem Height="0" Width="0" />
<TabItem Header="+" x:Name="AddTabButton"/>
</CompositeCollection>
</TabControl.ItemsSource>
</TabControl>
The code behind
private void Tab1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems.Contains(AddTabButton))
{
//Logic for adding a new item to the bound collection goes here.
string newItem = "Item " + (MyList.Count + 1);
MyList.Add(newItem);
e.Handled = true;
Dispatcher.BeginInvoke(new Action(() => Tab1.SelectedItem = newItem));
}
}
You could make a converter which appends the Add tab. This way the collection of tabs in you viewmodel will only contain the real tabs.
The problem is then how to know when the Add tab is selected. You could make a TabItem behavior which executes a command when the tab is selected. Incidentally I recommended this for another question just recently, so you can take the code from there: TabItem selected behavior
While I don't actually have the coded solution, I can give some insight on what is most likely the appropriate way to handle this in a WPF/MVVM pattern.
Firstly, if we break down the request it is as follows:
You have a sequence of elements that you want to display.
You want the user to be able to remove an individual element from the sequence.
You want the user to be able to add a new element to the sequence.
Additionally, since you are attempting to use a TabControl, you are also looking to get the behavior that a Selector control provides (element selection), as well as an area to display the element (content) which is selected.
So, if we stick to these behaviors you'll be fine, since the user interface controls can be customized in terms of look and feel.
Of course, the best control for this is the TabControl, which are you already trying to use. If we use this control, it satisfies the first item.
<TabControl ItemsSource="{Binding Path=Customers}" />
Afterwards, you can customize each element, in your case you want to add a Button to each element which will execute a command to remove that element from the sequence. This will satisfy the second item.
<TabControl ...>
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=CustomerId}" />
<Button Command="{Binding Path=RemoveItemCommand, Mode=OneTime,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type TabControl}}"
CommandParameter="{Binding}" />
</StackPanel>
</DataTemplate>
<TabControl.ItemTemplate>
</TabControl>
The last part is a bit more difficult, and will require you to actually have to create a custom control that inherits from the TabControl class, add an ICommand DependencyProperty, and customize the control template so that it not only displays the TabPanel, but right next to it also displays a Button which handles the DependencyProperty you just created (the look and feel of the button will have to be customized as well). Doing all of this will allow you to display your own version of a TabControl which has a faux TabItem, which of course is your "Add" button. This is far far far easier said than done, and I wish you luck. Just remember that the TabPanel wraps onto multiple rows and can go both horizontally or vertically. Basically, this last part is not easy at all.

Expanding grid (or similar) in WPF for a modeling program

I am pretty new to WPF, and in order to get some knowledge I decided to make a very simple UML modeling program, that basically offers the possibility to put some classes onto a canvas, connect them and move them around.
Now to the question:
I have been thinking about letting the classes I put on the canvas being a userControl I design. In my mind it would be something like a Grid, with some textboxes to represent properties, attributes and so on. The actual question is then, is my idea possible, or should I go with something completely different? My concern right now is how to implement the grid such that it can expand (add a row) under the right heading (Attribute/property..) when I want it to, and not be expanded to a maximum from the beginning.
I hope you can understand my question, and give me an idea to whether I should continue to implement it how I thought about, or do it using some other method.
You may wish to consider a ListView control, perhaps with an Expander, something like this:
<Canvas>
<Expander Header="Stuff"
MaxHeight="900"
Canvas.Left="202"
Canvas.Top="110">
<ListView Name="MyListView">
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="Add new thing"
Click="MenuItem_Click" />
</ContextMenu>
</ListView.ContextMenu>
<ListViewItem>
<StackPanel Orientation="Horizontal">
<Label>Name</Label>
<TextBox Text="Value" />
</StackPanel>
</ListViewItem>
<ListViewItem>Item two</ListViewItem>
<ListViewItem>Item three</ListViewItem>
</ListView>
</Expander>
</Canvas>
This will size as needed up to the max given. The list view items could contain any sort of content (not just text) as you can see above. You will want to learn a bit about Style and Control templates. WPF has IMHO a rather steep learning curve but there are a lot of learning resources on the web. Good luck.
In response to your comment, I'm adding additional information.
Anything you can do in XAML you can do in code behind (mostly XAML just calls framework objects). In this case I've added a context menu to the ListView control. This menu contains one item "Add new thing". There is a Click event for this item which is bound to the MenuItem_Click method in the code behind. I then added this method to the code:
void MenuItem_Click(object sender, RoutedEventArgs e) {
var lvi = new ListViewItem();
lvi.Content = String.Format("New thing {0}", DateTime.Now);
MyListView.Items.Add(lvi);
}
Now if you right click in the ListView you will see the "Add new thing" menu selection, left clicking it adds a new ListViewItem into the ListView (programmatically).

WPF ContextMenu for tray icon

I'm having a WPF application which I can minimize to tray. When I normal-click it, the window shows again.
Now I'm wondering how to create a simple ContextMenu?
The ContextMenu has to get filled with x options which onclick will run a function. For now I just need an 'Exit'-item linked to an 'Exit_Click' method.
Something I've tried is:
ContextMenu menu = (ContextMenu)this.FindResource("NotifierContextMenu");
menu.IsOpen = true;
menu doesn't know of any IsOpen value.
Other examples like to use a lot of different things. One of them requires me to create a HostManager for some reason.
I just need a simple ContextMenu. How can I achieve this?
As #H.B. mentioned Hardcodet's NotifyIcon is pretty darn good for WPF Taskbar icons. Sucks you don't get that out of the box with WPF but you might as well use it and address your issue than wait for Microsoft to fix it(They really should just add that library into standards)
Now to solve your issue(Using above solution):
Download the solution
Build the library
Add it to your source control if you have one and add a reference to it(Hardcodet.Wpf.TaskbarNotification.dll) in your project
Now in your MainWindow.xaml you could just have something like:
<Window ...
xmlns:tb="http://www.hardcodet.net/taskbar"
...>
...
<Grid>
<tb:TaskbarIcon>
<tb:TaskbarIcon.ContextMenu>
<ContextMenu>
<MenuItem Click="Exit_Click"
Header="Exit" />
</ContextMenu>
</tb:TaskbarIcon.ContextMenu>
</tb:TaskbarIcon>
...
</Grid>
</Window>
and MainWindow.xaml.cs with the click handler like you needed:
private void Exit_Click(object sender, RoutedEventArgs e) {
Application.Current.Shutdown();
}
I do recommend spending some time looking at the examples coming with the source code of the library to familiarize yourself with your available options. Trust me wpf has it way too easy when it comes to helper libraries. Try some of qt helper libraries and you'll know what "buried in there somewhere" literally means in opensource helpers.

ContextMenu won't go away after selection.

I have the following code that adds a context menu to a textbox on the UI. The user is supposed to be able to bring up the context menu and select a new units to be used. So the method CurrentUnits in my view model is bound to the textbox. I want a context menu populated by all the potential units. So the method Units in my view model returns a string[] of unit options; such as inches, cm, feet, meters, etc. When the user selects one the method NewUnits_Click is invoked. All works fine, however the contextmenu does not go away when the user selects a menu option. Pressing somewhere else on the screen like the application menu bar will then clear it. Has anyone else seen this problem, or see something wrong with the code below. It seems to have something to do with the ItemTemplate/DataTemplate I have, as creating an set of menu items by hand works fine.
<TextBlock Width="100" Text="{Binding CurrentUnits}" TextAlignment="Right">
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu ItemsSource="{Binding Units}">
<toolkit:ContextMenu.ItemTemplate>
<DataTemplate>
<toolkit:MenuItem Header="{Binding}" Click="NewUnits_Click" />
</DataTemplate>
</toolkit:ContextMenu.ItemTemplate>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
</TextBlock>
If you aren't using MVVM when working with WPF, I highly suggest you to start doing so. And even if you are not, I suggest that instead of binding to Click you instead bind on the Command property which wants something that implements ICommand. I think that the behavior you are getting is intended, buttons and menu items in WPF are intended to bind to Commands, it's not just WinForms 2.0.
Another solution would be to hide the Context menu in the code-behind. Perhaps this resource will help you in achieving that.

Categories

Resources