WPF MenuItem Content "Name" - wpf

I have a LOT of MenuItem(s), and I want to be able to change their "Content" so that it displays in the program. When I load up the program, their "Content Name" is set in a Setter I created.. but the only problem is that I have almost a hundred MenuItem objects, and I need their display names in the program to be different (not the setter's default). I could just create over 100 different "Setter"'s and change one line in them.. but that is very time consuming. Is there a simpler approach? I want to be able to do this in the XAML where I am declaring them. Is there a way to do this? I've been searching and trying different attempts, but nothing so far.. perhaps someone knows?
EDIT:
Sorry, Perhaps I am being a bit unclear..
I already have created the MenuItems and they are based on the Setter that I have created... The problem is.. I now want each one to still be based on that Setter, but to have a unique "Content"/Name that displays for the user...Currently, they all have the "Content" name given to them by the setter, but I am looking for a way to set each MenuItem's content name through XAML.. is this possible?
Thanks

You question is not clear. i think the best way to create hundreds of menu items is to create them from the code not in XAML. for example in a foreach loop. then you can give each of them a unique and meaningfull name. please describe your problem more clearly.
thanks

Now I understand your problem. generaly i think it would be a very bad idea to set the content property for each of your menuItems in the XAML file. Specialy when you are dealing with hundreds of items. a better way is to use the Data binding feature of WPF and DataTemplates, not to hardcode the menuItem names in the XAML file. I will propuse two solutions for your problem. first solution uses the code-behind approach to create menu items and then bind them to MainMenu's ItemsSource property without using dataTemplates. the following code is the code-behind code for the window:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MenuItems = new ObservableCollection<MenuItem>();
for (int i = 0; i < 40; i++)
{
MenuItem menuItem = new MenuItem();
menuItem.Header = "MenuItem" + i.ToString();
MenuItems.Add(menuItem);
}
MainMenu.DataContext = this;
}
public ObservableCollection<MenuItem> MenuItems
{
get;
set;
}
}
in this code first we created 40 number of menuItems and then we bind them to the DataContext property of the MainMenu object. the following code shows the XAML code of the windows including it's MainMenu object:
<Window x:Class="WpfApplication17.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Menu Grid.Row="0" Name="MainMenu" ItemsSource="{Binding MenuItems}">
</Menu>
</Grid>
</Window>
in this approch you can first create all of your menu items and their names in the code and after that bind them to the Menu object. then you can use styles to set common properties of the menu items.
but a better solution is to use dataTemplates as I did in the following code. in this approach first you created a class to store your menu item names. then with the help of the data template feature of WPF you can bind them to your MainMenu items. the code-behind of this solution is as follows:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MenuItems = new ObservableCollection<CustomMenuItem>();
MenuItems.Add(new CustomMenuItem("Item 1"));
MenuItems.Add(new CustomMenuItem("Item 2"));
MenuItems.Add(new CustomMenuItem("Item 3"));
MainMenu.DataContext = this;
}
public ObservableCollection<CustomMenuItem> MenuItems
{
get;
set;
}
}
public class CustomMenuItem
{
public string Name { get; set; }
public CustomMenuItem(string name)
{
Name = name;
}
}
in this code I used the CustomMenuItem class to store menuitem names. the MainWindows constructor is respossible for creating the menuitems but you can retrieve them from other sources, like a XML file of database. the XAML code for the MainWindow is like this:
<Window x:Class="WpfApplication17.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication17"
Title="MainWindow" Height="350" Width="525" >
<Window.Resources>
<DataTemplate DataType="{x:Type local:CustomMenuItem}">
<TextBlock Text="{Binding Name}"></TextBlock>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Menu Grid.Row="0" Name="MainMenu" ItemsSource="{Binding MenuItems}">
</Menu>
</Grid>
</Window>
this way you can retrieve your menuitem names fot\r example from a XML file or from other data sources and they are not hardcoded into you XAML file. then you can use the powerfull features of DataTemplates to view you menu items the way you like.

Related

Using named in reusable WPF control

I recentrly discovered "reusable controls" in WPF and I have a project where they seem to provide me with a solution to a problem I have.
Let me sketch the situation:
I need to make several UI elements. All of them share a common base, a common style/layout/template let's say, and they also share some common logic.
Next to that, all of these elements have some element-specific stuff.
You could say that I have some kind of inheritance here, but then for both XAML and CS.
The way I wanted to solve this, was by making an outer reusable element, I made a small example. The common part Is the Title label and the border. The element-specific UI can then be inserted into UserContent.
The code looks something like this (although it's simplified for the sake of brevity and conciseness, I also have an eventhandler and a routed event in my actual application):
ReusableControl.xaml
<UserControl x:Class="StackOverflowQuestion4.ReusableControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="root">
<Border BorderBrush="Black"
BorderThickness="1"
Width="400"
Height="200">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label Content="{Binding Title, ElementName=root}"
Grid.Row="0"/>
<ContentControl Content="{Binding UserContent, ElementName=root}"
Grid.Row="1"/>
</Grid>
</Border>
</UserControl>
ReusableControl.xaml.cs
using System.Windows;
using System.Windows.Controls;
namespace StackOverflowQuestion4
{
public partial class ReusableControl : UserControl
{
public ReusableControl()
{
InitializeComponent();
}
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(ReusableControl), new PropertyMetadata(string.Empty));
public object UserContent
{
get { return GetValue(UserContentProperty); }
set { SetValue(UserContentProperty, value); }
}
public static readonly DependencyProperty UserContentProperty =
DependencyProperty.Register("UserContent", typeof(object), typeof(ReusableControl), new PropertyMetadata(string.Empty));
}
}
Lovely, I can now use my special control in other parts of my code, and I can insert whatever I want into the UserContent field.
MainWindow.xaml
<Window x:Class="StackOverflowQuestion4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:StackOverflowQuestion4"
mc:Ignorable="d"
Title="MainWindow"
SizeToContent="WidthAndHeight">
<Grid Width="800"
Height="600">
<local:ReusableControl Title="Test">
<local:ReusableControl.UserContent>
<Rectangle Width="300"
Height="100"
Fill="Blue"/>
</local:ReusableControl.UserContent>
</local:ReusableControl>
</Grid>
</Window>
This works great, but the problem arises when I start to name things. Simply adding a name to an element inside of my ReusableControl causes a compilation error.
<Rectangle Width="300"
Height="100"
Fill="Blue"
Name="LolWhatAmIDoing"/>
I get the following error:
MC3093 - Cannot set Name attribute value 'LolWhatAmIDoing' on element 'Rectangle'. 'Rectangle' is under the scope of element 'ReusableControl', which already had a name registered when it was defined in another scope.
This seems like such a small issue, but I cannot find an easy solution to this problem.
I found this thread on the forum, but it does not really provide a solution.
Since I'm pretty new to all of this, I also don't really get what the issue is, so apologies if I'm slow minded.
Should I move to CustomControls?
What you show is a simple property assignment: you set the value of type Rectangle to the property ReusableControl.UserContent. It's important to understand that the Rectangle is not part of the visual tree at this point. It's a simple property value that is only accessible via the property and not via the visual tree.
This all happens in the scope of MainWindow.
But the Rectangle is not a member of this scope. The ReusableControl is adding it to its own visual subtree or scope by binding the value of ReusableControl.UserContent to a ContentControl. This is were the Rectangle exists i.e. is rendered in the visual tree.
It effectively doesn't exist in the scope of MainWindow. It effectively only exists inside the ReusableControl in the "shape" of a ContentControl. This means that the scope of ReusableControl is the only name scope where you can register a name for child elements. It's also the only scope where you can directly reference it (if it had been defined and registered in this scope).
If you understand this, then you understand that the Rectangle is currently trying to register a name in the wrong scope, a scope in which it doesn't exist.
As a consequence, you cannot directly refer to it in the scope of MainWindow. You would have to dig into the ContentTemplate of the UserControl (which is a ContentControl) in order to get the nested ContentControl that actually hosts the Rectangle.

Wpf Databound TextBlock not updating [duplicate]

This question already has an answer here:
Binding works without INotifyPropertyChanged, why?
(1 answer)
Closed 5 years ago.
I have a simple WPF window with a slider and two textblocks. As the slider moves it updates a data bound object. Now the first textblock updates while the second does not. Why?
You may say there is no INotifyPropertyChanged here. But then why is the first updating? I have pulled my hair enough. Please help.
My WPF app in all its glory is as follows.
<Window x:Class="DataTriggerDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DataTriggerDemo"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Slider x:Name="MySlider" Margin="5" Minimum="0" Maximum="100"
Value="{Binding TheValue}"/>
<TextBlock Grid.Row="1" Text="{Binding TheValue}" />
<TextBlock Grid.Row="2" Text="{Binding TheValueTwice}" />
</Grid>
</Window>
And now the code behind.
using System.Windows;
namespace DataTriggerDemo
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new DataObject();
}
}
public class DataObject
{
private int _theValue;
public int TheValue
{
get { return _theValue; }
set {
_theValue = value;
TheValueTwice = _theValue * 2;
}
}
private int _theValueTwice;
public int TheValueTwice
{
get {
return _theValueTwice;
}
set {
_theValueTwice = value;
}
}
}
}
Actually you are encountering a another hidden aspect of WPF, that's it WPF's data binding engine will data bind to PropertyDescriptor instance which wraps the source property if the source object is a plain CLR object and doesn't implement INotifyPropertyChanged interface. And the data binding engine will try to subscribe to the property changed event through PropertyDescriptor.AddValueChanged() method. And when the target data bound element change the property values, data binding engine will call PropertyDescriptor.SetValue() method to transfer the changed value back to the source property, and it will simultaneously raise ValueChanged event to notify other subscribers (in this instance, the other subscribers will be the TextBlocks within the ListBox.
Please refer to: https://social.msdn.microsoft.com/Forums/vstudio/en-US/9365bb6a-b411-4967-9a03-ae2a810fb215/data-binding-without-inotifypropertychanged?forum=wpf

WPF Caliburn Micro: Exchanging UserControls in a Window dynamically using ContentControl

This question is related to Add a usercontrol to caliburm micro dynamically.
I have read any other related threads before open this new thread, but I still don't understand and find no solution. Please accept my apology if some of you take this as duplicate.
I have a window (MainView) contains "main" Grid (aka LayoutRoot) with 2 columns.
On left column there are 2 buttons: "Display View 1" and "Display View 2".
If user click "Display View 1", the "Display1View" (is a UserControl contains TextBlock with Text "View 1") should be shown on the right column, replace the current one.
If user click "Display View 2", the "Display2View" (is a UserControl contains TextBlock with Text "View 2") should be shown on the right column, replace the current one.
My sample code contains following views and viewmodels:
MainView.xaml and MainViewModel.cs
Display1View.xaml and Display1ViewModel.cs
Display2View.xaml and Display2ViewModel.cs
In my sample code the ContentControl doesn't recognize the UserControl. What am I doing wrong? How to bind ContentControl correctly? Please feel free to modify my sample code. Thank you in advance
MainView.xaml
<Window x:Class="TestCaliMiContentControl.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Main View"
Width="525"
Height="350">
<Grid x:Name="LayoutRoot" ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30*" />
<ColumnDefinition Width="100*" />
</Grid.ColumnDefinitions>
<StackPanel x:Name="LeftNavPanel" Grid.Column="0">
<Button x:Name="Display1" Content="Display View 1" />
<Button x:Name="Display2" Content="Display View 2" />
</StackPanel>
<ContentControl x:Name="MainGridContent" Grid.Column="1" />
</Grid>
</Window>
MainViewModel.cs
public class MainViewModel : PropertyChangedBase
{
private ContentControl _mainGridContent;
public ContentControl MainGridContent
{
get { return _mainGridContent; }
set
{
_mainGridContent = value;
NotifyOfPropertyChange(() => MainGridContent);
}
}
public void Display1()
{
//MainGridContent = new Display1ViewModel(); // cannot convert source type error
}
public void Display2()
{
// MainGridContent = new Display2ViewModel(); // cannot convert source type error
}
}
Display1View.xaml
<UserControl x:Class="TestCaliMiContentControl.Display1View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
<Grid>
<TextBlock HorizontalAlignment="Center" FontSize="72"
Text="View 1"/>
</Grid>
</UserControl>
Display1ViewModel.cs
using System;
using System.Windows.Controls;
using Caliburn.Micro;
namespace TestCaliMiContentControl
{
public class Display1ViewModel : PropertyChangedBase {}
}
First, I would start by recommending you read the Caliburn.Micro documentation, specifically the part about Screens, Conductors, and Composition: http://caliburnmicro.com/documentation/composition
That being said, we can modify your code to get it working.
1) Since your MainViewModel is supposed to be conducting other items, it should descend from Conductor<T>. In this case, we will have it conduct the Caliburn Screen class.
public class MainViewModel : Conductor<Screen>
2) In MVVM, you view models should know nothing of your view. You should not see UI classes such as ContentControl. We could change your property to be of type Screen, but we actually don't need that property at all since we are using a conductor. So, remove the MainGridContent property and backing field.
3) Within your Display1 and Display2 methods, invoke Caliburn's conductor method ActivateItem to show the appropriate item.
public void Display1()
{
ActivateItem(new Display1ViewModel());
}
4) In your MainView.xaml you will need to bind your ContentControl to the conductor's active item property, which is, by convention, ActiveItem.
<ContentControl x:Name="ActiveItem" Grid.Column="1" />
5) Finally, since your conductor is conducting Screens, you need to make them screens. Screens are helpful because they have lifecycle and allow you to know when they are activated/deactivated. Do this for both Display1 and Display2.
public class Display1ViewModel : Screen {}
This should get you up and running.

Combobox Dropdown event triggers parent window Z index to change

I have a problem with a WPF business line application prototype that I do not know where to start to debug.
My Master Window (A) opens a "dialog" window using Dialog.Show(). The child window (B) has a combobox on it.
When the combobox is clicked (to expand and to show all the options) the entire second window (B) is hidden except the dropdownlist appearing from the combobox. The combobox isn't there, the window isn't there. Nothing is there except the dropdownlist and the master window behind it. If I click the master window then focus is once again set to window B and it shows as it should (because I set the Owner of window B to be window A).
To make it more interesting this bug is not consistent. It appears maybe 1 of 20 times I use the application, and when it starts appearing it can appear several times in a row and then stop happening again.
Possibly related is the fact that I think I have the same bug some times with MessageBoxes. When using MessageBox.Show() (Win forms msgbox) from a viewmodel when only the master window (A) is showing the box occationally appears behind the master window making it invisible for the user.
My application is using GalaSoft MvvmLight (if that could have anything to do with it) and quite a few telerik components. Other than that I am not sure what data to provide. I don't think anyone can tell me the source of the problem based on this information, but where would you start looking for the problem?
Update :
Good news! I have isolated the problem and found the combination of prerequisites for the phenomena to occur:
a) A component showing a PDF file is currently open anywhere withing the application.
b) A telerik component has been undocked and redocked.
I will include the code below, but I think the issue is buried deep within the RadDocking or WebBrowser component.
MainWindow
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void OpenWindowClick(object sender, RoutedEventArgs e)
{
var w = new TestWindow { Owner = this, DataContext = new TestViewModel()};
w.Show();
}
}
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:telerikDocking="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Docking"
x:Class="TelerikGridDockingVsBrowserVsWindowBug.MainWindow"
Title="MainWindow" Height="750" Width="925">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="200"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<telerikDocking:RadDocking Grid.Row="0">
<telerikDocking:RadSplitContainer>
<telerikDocking:RadPaneGroup>
<telerikDocking:RadPane Header="I make bugs combined with a webbrowser showing a pdf document">
<TextBlock Text="1. Drag me and dock on the same or another location"/>
</telerikDocking:RadPane>
</telerikDocking:RadPaneGroup>
</telerikDocking:RadSplitContainer>
</telerikDocking:RadDocking>
<Button Grid.Row="1" Click="OpenWindowClick" Content="2. Open window" Height="50" Margin="0,20"/>
<WebBrowser Grid.Row="2" Source="http://www.kb.nl/sites/default/files/docs/pdf_guidelines.pdf"/>
</Grid>
TestWindow :
public partial class TestWindow : Window
{
public TestWindow()
{
InitializeComponent();
}
}
<Window x:Class="TelerikGridDockingVsBrowserVsWindowBug.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TestWindow" Height="150" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock> 3. Click on the combobox.</TextBlock>
<ComboBox Grid.Row="1" ItemsSource="{Binding Options}" SelectedItem="{Binding SelectedOption}" Height="50"></ComboBox>
<TextBlock Grid.Row="2"> This window will then disappear behind its owner</TextBlock>
</Grid>
TestWindow ViewModel:
public class TestViewModel
{
public List<string> Options { get; set; }
public string SelectedOption { get; set; }
public TestViewModel()
{
Options = new List<string> { "String1", "String2" };
SelectedOption = Options.First();
}
}
I have an open ticket with Telerik about this, so I will update here if I find out anything.
I have been in your situation before... you have an unexplained problem... it doesn't make any sense... you've tried everything that you can think of... in a last ditch effort to fix the problem, you throw it out to all the experienced users here... then nothing... no responses... no help.
The thing is that if you can't find and fix the problem with all of your code right in front of you, then how can we fix it without any code?
All I can suggest is for you to try to locate exactly where the problem originates. You can do this by commenting out sections of code and then running your solution to see if that has removed the problem or whether it still exists. The fact that your problem only occurs infrequently will make this stage even more tricky.
However, with perseverance, you should be able to narrow down where the problem arises. Once you have managed to exclude enough of your code, the last step is to see if you re-create it in a much smaller new WPF project. If you manage to get to this stage, please come back and show your reduced code... then, we may be able to help you further.

WPF Single Selection Across Multiple ItemsControls

Part of my app has a month-view calendar interface, but I'm having trouble with item selection. The interface is set up so that each of the days in the view contains a ListBox of items, much like the month view in Outlook. The problem I'm experiencing is that I need to maintain a single item selection across all of the ListBoxes.
Below is a sample that should adequately describe my situation. I need to maintain a single selection between both ListBoxes.
<Window x:Class="StackOverflow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ListBox ItemsSource="{Binding Numbers}"
SelectedItem="{Binding SelectedObject"/>
<ListBox Grid.Column="1" ItemsSource="{Binding Dates}"
SelectedItem="{Binding SelectedObject"/>
</Grid>
</Window>
And the view model for the window:
class MainWindowViewModel : DependencyObject
{
public static readonly DependencyProperty SelectedObjectProperty =
DependencyProperty.Register("SelectedObject", typeof(object),
typeof(MainWindowViewModel),
new UIPropertyMetadata(null));
public ObservableCollection<int> Numbers { get; set; }
public ObservableCollection<DateTime> Dates { get; set; }
public object SelectedObject
{
get { return GetValue(SelectedObjectProperty); }
set { SetValue(SelectedObjectProperty, value); }
}
}
In this primitive example, I would expect that when the SelectedObject property of my view model gets set to an item that's not in one ListBox, the selection would be removed in that ListBox, but that doesn't happen. I understand that I can simply name each ListBox, and hook into the SelectionChanged event. I'd prefer to not have to do that with an entire month-view calendar. There has to be a better way.
In a previous iteration of the app, I was able to create a SelectionManager static class with an attached property that was used to maintain selection. However, I can't use this now as the classes I'm using for my items are not DependencyObjects, and I'd really prefer not to have to create DependencyObject wrapper classes as this will considerably complicate my architecture.
Thanks.
EDIT 1: Added a view model class as requested.
Bind both ListBoxes to the same collection (make an observable collection of DatesAndNumber objects or something along those lines) and use converters and/or data templates to get the desired output. Then you can simply set IsSynchronizedWithCurrentItem to true on both ListBoxes to get the desired effect.
Reference: http://msdn.microsoft.com/en-us/library/system.windows.controls.primitives.selector.issynchronizedwithcurrentitem.aspx
how about using ICollectionView and its associated filter ?
here is a link which explains it better
http://marlongrech.wordpress.com/2008/11/22/icollectionview-explained/
I figured it out by creating a separate selection manager class and an ISelectable interface. You can read about the details on CodeProject.

Resources