How to check against Content name of the ListBoxItem? - wpf

I was able to toggle visibility of menuItem based on the property of another content (data in the datagrid). I need to be able to achieve the same with listbox items. I have hard time doing so. I am wondering if someone can help with that: Any help is highly appreciated!
The way it worked with menuItems:
XAML:
<SplitButton:MenuButton x:Name="test" Content="Test">
<SplitButton:MenuButton.ButtonMenuItemsSource>
<toolkit:MenuItem x:Name="item1" Header="Item1" />
<toolkit:MenuItem x:Name="item1" Header="Item2" />
</SplitButton:MenuButton.ButtonMenuItemsSource>
I have problem with setting the same var for lisboxitem listbox. It is working perfect with MenuItems.
var item1Task = test.ButtonMenuItemsSource.OfType<ListBoxItem>().Where(temp => temp.Name == "item1").First();
I need to convert this coede line of setting var in a way that it will work with ListBoxItem:
Listbox XAML:
<toolkit:Expander x:Name="test" Header="Test">
<Border x:Name="Border">
<ListBox x:Name="List">
<ListBoxItem x:Name="item1" Content="Item1" />
<ListBoxItem x:Name="item2" Content="Item2" />
</ListBox>
I cannot use OfType within listbox content. I tried something like that:
var item1Task = List.OfType<ListBoxItem>().Where(temp => temp.Content == "item1").First();
I hope it is possible. It is working great with the previous control, but I need to do the same using listbox. Thank you in advance for the help.

Do you need to find ListBoxItem named "item1" or which has "item1" as Content? To do the first, you may try this query:
var item1Task = List.Items.Cast<ListBoxItem>().First(temp => temp.Name == "item1");
If you need to find content, use this:
var item1Task = List.Items.Cast<ListBoxItem>().First(temp => temp.Content == "Item1");
You should give more attention to case sensitivity when comparing strings. Also there is no need to use Where and First together, because First already has conditional version.

Related

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

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.

DataBinding to a List in silverlight

I've got a Grid which contains a TextBlock. The Grid's DataContext is of type List<MyClass>, and I'd like to bind the TextBlock.Text property to the MyClass.MyProperty property of first element in the List. I tried something like:
<Grid x:Name="RootLayout">
<TextBlock Text="{Binding [0].MyProperty}" />
</Grid>
But of course, that did not work. What's the right way of doing this?
Edit:
I'm going to try and make my explanation more clear. I've got multiple elements in the grid, each of which binds to a different item in the list. The items are laid out in a customized manner which cannot be accomplished by a GridView or ListBox. One of the items in the Grid is the TextBlock, and I'd like to bind its Text property to a property of the first element in the list. Once I know how to do that, I can extend that knowledge to add bindings to the rest of the elements in the grid.
Edit 2:
Turns out, my code works just fine in Silverlight. My project is actually a WinRT project, but I figured I'd get quicker answers if I tagged it as Silverlight, since databinding is supposed to work the same. I'm assuming this is a bug in WinRT, so I'll just have to find a workaround for it :(
I'm not sure I get why you want to do this, but you could create a property that returns what you want from the item in the list like so:
public string MyBindingProperty
{
get { return MyList != null && MyList.Count > 0 ? MyList[0].MyProperty : "Error Text"; }
}
Then you'd bind to MyBindingProperty:
<TextBlock Text="{Binding MyBindingProperty}" />
EDIT
I was wrong in saying you can't get at the items in the List - my bad. Your binding should look like this:
<TextBlock Text="{Binding [0].MyProperty}" />
If you need me I'll be in the corner enjoying my humble pie.
I am not an expert of SL but I think your are using the wrong Grid object; try with DataGrid in this way:
<data:DataGrid x:Name="targetDataGrid">
<data:DataGrid.Columns>
<data:DataGridTextColumn Header="MyProperty"
Binding="{Binding MyProperty}" />
</data:DataGrid.Columns>
</data:DataGrid>
also see here for more details: Defining Silverlight DataGrid Columns at Runtime
Edit: then go this way:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding MyProperty}" />
</Grid>
found here: http://msdn.microsoft.com/en-us/library/cc278072%28v=VS.95%29.aspx scroll donw the article...

ListView selecting single item , wpf

i have a listview and it's items source is a list . I want a user to pick only one item . When I set SelectionMode of the listview to single , the user can still select several items and it seems like the listview is going crazy and selects items that user didn't select... looks very strange... can anyone know what could be the problem?
I cann't paste here a screenshot , i don't have the paste option.....
this is a xaml -
<StackPanel MinWidth="600" Margin="0,0,0,10" HorizontalAlignment="Left" Width="600">
<GroupBox Header="Command Queue" BorderThickness="0" Foreground="CornflowerBlue">
<Border BorderThickness="1.5" CornerRadius="10">
<ListView SelectionMode="Single" Background="Transparent" BorderThickness="0" Margin="5" Name="ListView_CmdQ" ItemsSource="{Binding}" MaxHeight="450" FontFamily="verdana" FontSize="12">
</ListView>
</Border>
</GroupBox>
</StackPanel>
Do the items in your list appear more than once? I've seen this problem before where you have something like this:
var a = new Thing();
var b = new Thing();
var myList = new List<Thing>();
myList.Add(a);
myList.Add(b);
myList.Add(a);
myList.Add(b);
If you were to bind a ListView to the myList, you'd get the behaviour you've described. I think basically it's to do with the fact that multiple items in the list match the SelectedItem, so the styling of the list gets a bit confused. One way around it is to wrap each item in another class:
var myList = new List<WrappedThing>();
myList.Add(new WrappedThing((a));
myList.Add(new WrappedThing((b));
myList.Add(new WrappedThing((a));
myList.Add(new WrappedThing((b));
... which means that each item in the list is unique, even though the item they're wrapping may not be.
If your list_listItems contains the same string twice, you get this behavior. This happens with value types and reference strings. You should probably wrap each string in a TextBlock and put that in the listview.
It looks like this is reported as a bug still active (since 2007) here.

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

wpf control template

I have a very simple case that I think would benefit from using templates (but I'm not sure, which is why I'm asking). All the templating examples I've seen either assume more knowledge than I have, are too specific to be of much use to a total newb like myself, or contain lots of ancillary stuff that makes it hard to identify what's part of the template.
Here's the setup:
I have two labels side-by-side, with the first label populated with the name of a field, and the second label populated with the value of the field.
Here is the XAML I currently have in my app (many, many times):
<StackPanel Style="{StaticResource horizontalStackerStyle}">
<Label Style="{StaticResource labelStyle}">Field One:</Label>
<Label Style="{StaticResource valueStyle}" Name="field1"
Content="{Binding dataObject.field1}" />
</StackPanel>
I would like to create a template such that I could write XAML like this:
<CustomControlOrWhatever
FieldName="Field One:"
FieldValue="{Binding dataObject.field1}"/>
I have a feeling I can do this with some kind of template. One benefit of which would be that I don't need to keep specifying the styles over and over. Am I correct? How would I do this?
Thanks in advance!
UPDATE:
Still haven't found an answer to this. I chose a possible solution using Dependency Properties, and tried to ask a clarifying question here. Well, the first responder said that I don't actually need to clutter up my code behind with DP nonsense, so I changed it again--and it still doesn't work. Can anyone come up with a working solution? This seems like it should be so simple.
Just to be clear: this only needs to be one-way binding with values updated every few seconds.
What you're asking for is basically a user control.
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SomeNameSpace.SomeControlName">
<Grid x:Name="LayoutRoot">
<StackPanel Style="{StaticResource horizontalStackerStyle}">
<Label Style="{StaticResource labelStyle}" x:Name="FieldNameLbl"></Label>
<Label Style="{StaticResource valueStyle}" x:Name="ValueLbl">
</StackPanel>
</Grid>
</UserControl>
In the code behind, you'd need to expose two properties that would set the value of the controls.
public string FieldName
{
get { return FieldNameLbl.Text; }
set { FieldNameLbl.Text = value; }
}
public string FieldValue
{
get { return ValueLbl.Text; }
set { ValueLbl.Text = value; }
}
And then to call that you can put this at the top of your window/page with the rest of your declarations:
xmlns:Controls="clr-namespace:SomeNameSpace"
and then you can insert the control into your window/page like this:
<Controls:NameOfYourControl FieldName="Field One:" FieldValue="{Binding dataObject.field1}"/>
You could create a UserControl called FieldControl and define backing (automatic) properties for FieldName and FieldValue. (Normal properties would be fine, so long as you only need to bind once, which is probably the case.)
The XAML code might look like:
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="foo.bar">
<StackPanel Style="{StaticResource horizontalStackerStyle}">
<Label Style="{StaticResource labelStyle}" Content="{Binding Path=FieldName, Mode=OneTime, StringFormat='{0}: '}"/>
<Label Style="{StaticResource valueStyle}" Content="{Binding Path=FieldValue, Mode=OneTime}" />
</StackPanel>
</UserControl>
Hope that helps.
What you want to do is similar to the discussion about putting images on a button with a simple way of specifying the path, like <Button MyImage="foo.jpg" />. Follow this article for the actual details.
To summarize:
One obvious way would be to create an UserControl containing your two labels and exposing the two properties. Not much templating here.
The most WPFish solution seems to be to use two Attached Properties on one of the labels (say the value), and provide a control template for it that includes the other label (the description). In the template, you bind each label text to the corresponding attached property value.

Resources