WPF Expander, get the Header name out of a child element - wpf

I have a WPF Expander, such as this:
<Expander Canvas.Left="251" Canvas.Top="425" Header="expander1" Height="100" Name="expander1">
<Grid>
<StackPanel Margin="10,4,0,0">
<CheckBox Margin="4" Content="Option 1" Checked="chk_DoThis" />
<CheckBox Margin="4" Content="Option 2" Checked="chk_DoThis" />
<CheckBox Margin="4" Content="Option 3" Checked="chk_DoThis" />
</StackPanel>
</Grid>
</Expander>
When a checkbox is clicked, I fire off a 'Checked' event.
Is there some way to pull out a string that contains the 'Header' of the Expander? In this example, I want to pull out 'expander1' and assign that to a string.
I tried a few ways of doing this and couldn't get it to work. I have done this same concept using TreeViewItems and using a Header.Parent.ToString() to get what I wanted. No luck here. This is what I'm referring to:
string child = ((TreeViewItem)((TreeViewItem)((TreeView)sender).SelectedItem)).Header.ToString();
Does anyone know of a way I could do this for my Expander example. Googling and searching this site has yielded no return. It's probably something easy and I'm just overlooking it.
Thanks to anyone that has some ideas.

You can easily get the Expander from the CheckBox. Just iterate to the VisualTree and get the Top most parent's Expander. I just simply did only one parent in the below example.
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
Expander expander = (Expander)checkBox.Parent;
if (expander != null)
{
string str = expander.Header.ToString();
Debug.WriteLine(str);
}
}
I hope it will help you.

Related

WPF - How to restyle ComboBox to remove the textbox/editbox and replace with static text

I want to restyle a WPF ComboBox which is formatted to be a drop-list type, BUT remove the selected TextBox which gets populated with the selected contents and just replace it with some static text and an image which remains constant, simulating a button like look.
So in effect it becomes a button-drop-list, so when I select an item from the drop list, I can populate another control via command bindings with its selected value and the button style remains.
Basically something like this crude picture I've hacked together.
I've seen examples of button with context menus, but I don't like the idea, and a ComboBox fits my needs perfectly in terms of function and easy command and data binding.
I know it can be done, but I lost faith in my ablity after reading overly confusing examples based on other controls. I couldn't find an example detailing my needs to learn from.
Cheers
DIGGIDY
After much playing around, I decided the better option was to go for a button with bound context menu, this worked out to be the better solution in the end.
Thanks for your help Marc.
I had got the same problem and actually, it's simple.
Just put a read-only ComboBox with a SelectionChanged event.
You put in index 0 your static text.
Now, when the user is selecting something, get the selected item and then, set the SelectedIndex to 0. So you got the item the user selected but the displayed text is the same.
See:
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBox combo = (ComboBox)sender;
if (combo.SelectedIndex > 0)
{
// Do your stuff here...
// Then
combo.SelectedIndex = 0;
}
}
[EDIT] According to me, I prefer my previous answer. So make sure you, reader, that my previous answer doesn't match your expectations. [/EDIT]
Another answer is to put your object above the ComboBox and then catch the MouseDown event from this object and dropped down the ComboBox. I used a read-only TextBox in my example.
See:
<Grid>
<ComboBox x:Name="Combo" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="120">
<ComboBoxItem Content="TEST" />
<ComboBoxItem Content="TEST1" />
<ComboBoxItem Content="TEST2" />
<ComboBoxItem Content="TEST3" />
</ComboBox>
<TextBox HorizontalAlignment="Left" Height="23" Margin="10,10,0,0" Text="TextBox" VerticalAlignment="Top" Width="120" IsReadOnly="True" PreviewMouseDown="TextBox_PreviewMouseDown"/>
</Grid>
And then the code behind:
private void TextBox_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
e.Handled = true; // Prevents the event.
Combo.IsDropDownOpen = true; // Drops down the ComboBox.
}
It works fine for me.

ScrollViewer Control in Wpf

<ScrollViewer VerticalScrollBarVisibility="Auto" >
<TextBlock Text="{Binding Status}"
HorizontalAlignment="Center" />
</ScrollViewer>
The Status always I'm adding to it, that is to say that in the code behind always I do this thing:
Status+= Environment.NewLine + "Hi";
And the ScrollViewer increasing, but the Status that I see is the first one, when I want to see the last one I need to scroll underneath to see it, my question is: how I can made the Scrollviewer scrolling underneath automaticaly?, that meaning I want always see the last status not the first.
Sorry about the broken english.
Name your ScrollViewer so you can access it in the code behind, like this:
<ScrollViewer x:Name="MyScrollViewer" VerticalScrollBarVisibility="Auto" >
<TextBlock Text="{Binding Status}"
HorizontalAlignment="Center" />
</ScrollViewer>
Then you can do this:
Status+= Environment.NewLine + "Hi";
MyScrollViewer.ScrollToEnd();
With the way I do MVVM, I have access to my ViewModel from my View, so when the View first loads, I'd subscribe to the PropertyChanged event on my ViewModel as so:
MyViewModel.PropertyChanged += ViewModelChanged;
and then in the ViewModelChanged callback I'd have this:
private void ViewModelChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Status")
MyScrollViewer.ScrollToEnd();
}
Every time the ViewModel's Status property changes, the ScrollViewer will now scroll to end. Just remember to unsubscribe from MyViewModel.PropertyChanged when you leave that screen to avoid a memory leak.

AutoComplete list update?

Evening all,
I have a basic autocompletebox using silverlight5.
The aim is to be able to search through a list of people and remove certain people from this list via checkboxes above.
On the checkbox event the list is modified but this is not reflected in the autocompletebox.
.xaml:
<StackPanel Orientation="Vertical" x:Name="LayoutRoot" Background="Transparent">
<sdk:Label Content="Filter By:" FontSize="12" Name="label1" Margin="10,10,10,5" />
<CheckBox Content="Students" Height="16" Name="checkBox1" Margin="10,5,10,0" Checked="checkBox1_Checked" Unchecked="checkBox1_Checked"/>
<CheckBox Content="Staff" Height="16" Name="checkBox2" Margin="10,5,10,0" Checked="checkBox2_Checked" Unchecked="checkBox2_Checked"/>
<CheckBox Content="Guest" Height="16" Name="checkBox3" Margin="10,5,10,10" Checked="checkBox3_Checked" Unchecked="checkBox3_Checked"/>
<sdk:AutoCompleteBox x:Name="peoplelist"/>
</StackPanel>
Code behind:
public CustomerFilterControl()
{
InitializeComponent();
//_viewModel.Initialize(); initial loading of context data, populate dropdowns etc
people.Add("Student 1");
//.....................add more
peoplelist.Itemssource = people;
}
Checkbox methods:
private void checklist()
{
if (checkBox1.IsChecked.Value)
{
people.Clear();
people.Add("Guest 1");
//.................... add more
peoplelist.DataContext = people;
}
Lots of searching has pointed me to many work around for earlier versions of sliverlight but I'm practically going around in circles at this point.
Could anyone point me in the right direction to getting this functioning?
Replace List<string> by ObservableCollection<string>
This generic raises the CollectionChangedEvent so bound controls know they need to update.

How to dynamically add MenuItems (with a header) to a WPF menu

[Edit #3] - to anyone reading this question: do not under any circumstance use the approach outlined in this question. It is a Coding Horror. I freely admit this, knowing that all programmers have worked themselves into a corner in the past, and (especially when learning a new technology) we all have been led astray by other, well-meaning developers on the interweb. Read the answer by Robert first, then read this question. Please.
[Edit #2b]
I apologize for the length of this question - there is a question in here (at the end!), but I wanted to make sure the source code was explicit. Anyway.
[Edit #2] - question title changed to more accurately reflect the... question.
[Edit] - I've updated some more of the history as to how I ended up at the design / code that I did here: Obligatory Blog Post. If it helps clarify the question below, feel free to read it...
Original question
The application I'm working on uses Prism and WPF, with a number of modules (currently 3), one of which hosts the application menu. Originally, the menu was static with hooks into CompositeCommand / DelegateCommands, which worked great for routing button presses to the appropriate presenter. Each MenuItem used a StackPanel in its header to display the content as a combination of an image and a text label - which was the look I was going for:
<Menu Height="48" Margin="5,0,5,0" Name="MainMenu" VerticalAlignment="Top" Background="Transparent">
<MenuItem Name="MenuFile" AutomationProperties.AutomationId="File">
<MenuItem.Header>
<StackPanel>
<Image Height="24" VerticalAlignment="Center" Source="../Resources/066.png"/>
<ContentPresenter Content="Main"/>
</StackPanel>
</MenuItem.Header>
<MenuItem AutomationProperties.AutomationId="FileExit" Command="{x:Static local:ToolBarCommands.FileExit}">
<MenuItem.Header>
<StackPanel>
<Image Height="24" VerticalAlignment="Center" Source="../Resources/002.png"/>
<ContentPresenter Content="Exit"/>
</StackPanel>
</MenuItem.Header>
</MenuItem>
</MenuItem>
<MenuItem Name="MenuHelp" AutomationProperties.AutomationId="Help" Command="{x:Static local:ToolBarCommands.Help}">
<MenuItem.Header>
<StackPanel>
<Image Height="24" VerticalAlignment="Center" Source="../Resources/152.png"/>
<ContentPresenter Content="Help"/>
</StackPanel>
</MenuItem.Header>
</MenuItem>
</Menu>
Unfortunately, the application has gotten a bit more complex and it is desireable to have other modules register themselves with the menu - hence, I've been looking at making the menu dynamic. The goal is to have other modules (through a service) be able to add commands to the menu at will - for example, Module A will add a menu item in the Toolbar module that calls a handler in Module A. There's a few excellent articles out there on this subject - the two I've looked at are Building a Databound WPF Menu Using a HierarchicalDataTemplate and WPF Sample Series - Databound HierarchicalDataTemplate Menu Sample. Following the advice in the article, I have managed to make a dynamically constructed menu with no obvious data binding problems - it can create a menu with items linked backed to my presentation model, reflecting the structure of an ObservableCollection in the presentation model
Currently, my XAML looks like the following:
<UserControl x:Class="Modules.ToolBar.Views.ToolBarView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:model="clr-namespace:Modules.ToolBar.PresentationModels"
xmlns:local="clr-namespace:Modules.ToolBar">
<UserControl.Resources>
<model:ToolBarPresentationModel x:Key="modelData" />
<HierarchicalDataTemplate DataType="{x:Type model:ToolbarObject}"
ItemsSource="{Binding Path=Children}">
<ContentPresenter Content="{Binding Path=Name}"
Loaded="ContentPresenter_Loaded"
RecognizesAccessKey="True"/>
</HierarchicalDataTemplate>
</UserControl.Resources>
<UserControl.DataContext>
<Binding Source="{StaticResource modelData}"/>
</UserControl.DataContext>
<Menu Height="48" Margin="5,0,5,0" Name="MainMenu" VerticalAlignment="Top" Background="Transparent"
ItemsSource="{Binding}">
</Menu>
</Grid>
</UserControl>
The code behind for the view does the heavy lifting in the ContentPresenter_Loaded method:
private void ContentPresenter_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
ContentPresenter presenter = sender as ContentPresenter;
if (sender != null)
{
DependencyObject parentObject = VisualTreeHelper.GetParent(presenter);
bool bContinue = true;
while (bContinue
|| parentObject == null)
{
if (parentObject is MenuItem)
bContinue = false;
else
parentObject = VisualTreeHelper.GetParent(parentObject);
}
var menuItem = parentObject as MenuItem;
if (menuItem != null)
{
ToolbarObject toolbarObject = menuItem.DataContext as ToolbarObject;
StackPanel panel = new StackPanel();
if (!String.IsNullOrEmpty(toolbarObject.ImageLocation))
{
Image image = new Image();
image.Height = 24;
image.VerticalAlignment = System.Windows.VerticalAlignment.Center;
Binding sourceBinding = new Binding("ImageLocation");
sourceBinding.Mode = BindingMode.TwoWay;
sourceBinding.Source = toolbarObject;
image.SetBinding(Image.SourceProperty, sourceBinding);
panel.Children.Add(image);
}
ContentPresenter contentPresenter = new ContentPresenter();
Binding contentBinding = new Binding("Name");
contentBinding.Mode = BindingMode.TwoWay;
contentBinding.Source = toolbarObject;
contentPresenter.SetBinding(ContentPresenter.ContentProperty, contentBinding);
panel.Children.Add(contentPresenter);
menuItem.Header = panel;
Binding commandBinding = new Binding("Command");
commandBinding.Mode = BindingMode.TwoWay;
commandBinding.Source = toolbarObject;
menuItem.SetBinding(MenuItem.CommandProperty, commandBinding);
}
}
}
As you can see, I'm attempting to recreate the StackPanel / Image / Name combination of the original menu, just doing so in the code behind. Attempting to do this has not worked out so well - while the menu objects are certainly being created, they don't "appear" as anything other than blank, clickable objects - the StackPanel, Image, Name, etc. aren't being rendered. Interestingly enough, it also is causing the original text in the ContentPresent in the HierarchicalDataTemplate to be erased.
The question then, is there a way to set a MenuItem's Header property in the Load event such that it will display on the UserControl properly? Is the fact that the items in the header are not being displayed indicative of a DataBinding problem? If so, what would be the proper way to bind the Header to a transient object (the StackPanel that was created in the load event handler)?
I'm open to changing anything in the code above - this is all sort of prototyping along, trying to figure out the best way to handle dynamic menu creation.
Thanks!
I'll confess that I haven't dug quite as deep into your example as maybe I should, but whenever I see code-behind that's searching the visual tree, I think, could this be handled more explicitly in a view model?
It seems to me in this case that you could come up with a pretty straightforward view model - an object exposing Text, Image, Command, and Children properties, for instance - and then create a simple data template that for presenting it as a MenuItem. Then anything that needs to alter the contents of your menus manipulates this model.
Edit:
Having looked at what you're up to in more detail, and the two examples you've linked to in your blog post, I am banging my head against the desk. Both of those developers appear to be under the misapprehension that the way to set properties on the menu items that are being generated by the template is to search through the visual tree in the ContentPresenter.Load event after they're created. Not so. That's is what the ItemContainerStyle is for.
If you use that, it's quite straightforward to create dynamic menus of the type you're describing. You need a MenuItemViewModel class that has INotifyPropertyChanged implemented and exposes these public properties:
string Text
Uri ImageSource
ICommand Command
ObservableCollection<MenuItemViewModel> Children
Using this:
<Menu DockPanel.Dock="Top" ItemsSource="{DynamicResource Menu}"/>
where the ItemsSource is an ObservableCollection<MenuItemViewModel>, and using this template:
<HierarchicalDataTemplate DataType="{x:Type local:MenuItemViewModel}"
ItemsSource="{Binding Path=Children}">
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command"
Value="{Binding Command}" />
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ImageSource}" />
<Label Content="{Binding Text}" />
</StackPanel>
</HierarchicalDataTemplate>
the menus in the window exactly represent what's in the collection, and are dynamically updated as items are added and removed, both to the top-level items and to the descendants.
There's no clambering about in the visual tree, no manual creation of objects, no code-behind (other than in the view model, and in whatever populates the collection in the first place).
I've built a pretty thoroughly worked example of this; you can download the project here.
Another possible approach could be having the Menu be a region and agree on a convention so all views added to that region have a ViewModel with a property named MenuHeader. That way, the region adapter can simply get the menu header from the View's Data Context, and set it to the item when adding it.
Something similar is done in Prism with views added to a Tab Region. You can read more here.
I hope this provides some useful guidance.
Thanks,
Damian

Setting focus on a ListBox item breaks keyboard navigation

After selecting ListBox item programmatically it is needed to press down\up key two times to move the selection. Any suggestions?
View:
<ListBox Name="lbActions" Canvas.Left="10" Canvas.Top="10"
Width="260" Height="180">
<ListBoxItem Name="Open" IsSelected="true" Content="Open"></ListBoxItem>
<ListBoxItem Name="Enter" Content="Enter"></ListBoxItem>
<ListBoxItem Name="Print" Content="Print"></ListBoxItem>
</ListBox>
Code:
public View()
{
lbActions.Focus();
lbActions.SelectedIndex = 0; //not helps
((ListBoxItem) lbActions.SelectedItem).Focus(); //not helps either
}
Don't set the focus to the ListBox... set the focus to the selected ListBoxItem. This will solve the "two keyboard strokes required" problem:
if (lbActions.SelectedItem != null)
((ListBoxItem)lbActions.SelectedItem).Focus();
else
lbActions.Focus();
If your ListBox contains something else than ListBoxItems, you can use lbActions.ItemContainerGenerator.ContainerFromIndex(lbActions.SelectedIndex) to get the automatically generated ListBoxItem.
If you want this to happen during window initialization, you need to put the code in the Loaded event rather than into the constructor. Example (XAML):
<Window ... Loaded="Window_Loaded">
...
</Window>
Code (based on the example in your question):
private void Window_Loaded(object sender, RoutedEventArgs e)
{
lbActions.Focus();
lbActions.SelectedIndex = 0;
((ListBoxItem)lbActions.SelectedItem).Focus();
}
You can do this easily in XAML too. Please note that this will set logical focus only.
For example:
<Grid FocusManager.FocusedElement="{Binding ElementName=itemlist, Path=SelectedItem}">
<ListBox x:Name="itemlist" SelectedIndex="1">
<ListBox.Items>
<ListBoxItem>One</ListBoxItem>
<ListBoxItem>Two</ListBoxItem>
<ListBoxItem>Three</ListBoxItem>
<ListBoxItem>Four</ListBoxItem>
<ListBoxItem>Five</ListBoxItem>
<ListBoxItem>Six</ListBoxItem>
</ListBox.Items>
</ListBox>
</Grid>
Seems that there are two levels of Focus for ListBox control: ListBox itself and ListBoxItem. Like Heinzi said, directly set Focus for the ListBoxItem will avoid the case that you have to click twice on direction key in order to go through all ListBoxItems.
I found out this after several hours work, now it works perfect on my APP.

Resources