I have a ContextMenu and a ColumnHeaderStyle defined in Window.Resource section which I use-it to a DataGrid ColumnHeader. My code is something like this:
<ContextMenu x:Key="cm_columnHeaderMenu"/>
<Style x:Key="DefaultColumnHeaderStyle" TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="ContextMenu" Value="{StaticResource cm_columnHeaderMenu}" />
</Style>
<DataGrid Grid.Column="2" Grid.Row="1" x:Name="dgridFiles" IsReadOnly="True"
ColumnHeaderStyle="{StaticResource DefaultColumnHeaderStyle}">
I want to know if I can (and if the answer it true, then HOW I can I do it) bind the ContextMenu Visibility property to same control ContextMenu Items.Count > 0 property.
Initially based on some other treeView control selections made there shoud be no items in the context menu, but i wish to add dinamically items in ContextMenu based on selection in treeView. This part is done, the context has those items. On some selections there are no-items, but still on the grid it appears an empty ContextMenu. So I believe the easiest part it would be to bind the Visibility to Items.Count property of the same control.
Sorry if my english is not good enough, I'll try to explain better if i didnt make clear 1st time.
you want to bind via RelativeSource, especially the Self mode.
I think by reading this or this you will be able to achieve your goal.
Then you'll need a binding converter to convert the integer values to the matching type and values of the Visibility property. You'll find a short tutorial here.
Regards
Using this you can bind to the property in the same control
Visibility="{Binding Path=Items.Count, RelativeSource={RelativeSource Self}}"
You also have to use a converter to achieve what you want.
Just in case you need this
Try a converter to convert the value of the item count to a boolean. So you'll end up with something like
<ContextMenu Visibility={Binding RelativeSource={RelativeSource Self},
Converter={StaticResource ItemsToVisibilityConverter}, Path=Items.Count}} />
If that doesn't work, try this with data triggers (you still need a converter anyway, and this shows a converter at work):
http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/a8ad8c14-95aa-4ed4-b806-d0ae874a8d26/
Related
Today I'm having trouble passing values from a parent control down to the properties of a child control in a list.
I have a custom control which I've made which functions as a Thumbnail Check Box. Essentially it's just a checkbox wrapped around an image with some nice borders. It's all wrapped up into a DLL and deployed as a custom control
If I want to use a single instance of the control, I can do so like this...
<tcb:ThumbnailCheckBox IsChecked="True"
ImagePath="D:\Pictures\123.jpg"
CornerRadius="10"
Height="{Binding ThumbnailSize}"
Margin="10" />
Code Listing 1 - Single Use
This works great, and easily binds to ThumbnailSize on my ViewModel so I can change the size of the image in the control however I want.
The problem is when I want to expand the use of this control into a list, I'm running into a few problems.
To begin, I've styled the ListBox control to meet my needs like so...
<Style TargetType="{x:Type ListBox}"
x:Key="WrappingImageListBox">
<!-- Set the ItemTemplate of the ListBox to a DataTemplate
which explains how to display an object of type BitmapImage. -->
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<tcb:ThumbnailCheckBox ImagePath="{Binding ImagePath}"
IsChecked="{Binding Selected}"
Height="{TemplateBinding utilities:MyAttachedProperties.ImageSize}"
CornerRadius="8"
Margin="10">
</tcb:ThumbnailCheckBox>
</DataTemplate>
</Setter.Value>
</Setter>
<!-- Swap out the default items panel with a WrapPanel so that
the images will be arranged with a different layout. -->
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<!-- Set this attached property to 'Disabled' so that the
ScrollViewer in the ListBox will never show a horizontal
scrollbar, and the WrapPanel it contains will be constrained
to the width of the ScrollViewer's viewable surface. -->
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility"
Value="Disabled" />
</Style>
Code Listing 2 - ListBox Style
And I call it like this from my main view...
<ListBox ItemsSource="{Binding DirectoryPictures}"
Grid.Row="1"
Style="{DynamicResource WrappingImageListBox}"
Background="Transparent"
util:MyAttachedProperties.ImageSize="500"/>
Code Listing 3 - Main Call
This works exactly as I'd like, except for the ImageSize property. Both ImagePath and Selected are properties of the individual list items being bound to the ListBox.
As you can see, I created an attached property to try to pass the value (500), but it doesn't seem to be working. I should note that I think the style I've created is correct because the elements use the default value.
public static class MyAttachedProperties
{
public static double GetImageSize(DependencyObject obj)
{
return (double)obj.GetValue(ImageSizeProperty);
}
public static void SetImageSize(DependencyObject obj, double value)
{
obj.SetValue(ImageSizeProperty, value);
}
public static readonly DependencyProperty ImageSizeProperty =
DependencyProperty.RegisterAttached(
"ImageSize",
typeof(double),
typeof(MyAttachedProperties),
new FrameworkPropertyMetadata(50D));
}
Code Listing 4 - Attached Property
The 50D specified on the last line is applying to the listed control. If I change it, and recompile, the end result changes. But the sent value of 500 I specified in my ListBox Main call (listing 3) is not ever sent. Of course, I would eventually like to change the 500 into a bound property on my view model, but I won't do that until I get it working with an explicit value.
Can someone help me figure out how to send a value from my main ListBox call (listing 3) and apply it to the individual items that are populated by the template? The other properties I have work, but they are a properties of each item in the List I'm binding to the ListBox, whereas ImageSize is not.
EDIT To address First Response
This seems to be working, but it's kind of peculiar. My listbox is now being called like so...
<ListBox ItemsSource="{Binding DirectoryPictures}"
Grid.Row="1"
Style="{DynamicResource WrappingImageListBox}"
Background="Transparent" />
And I've changed my style to the code you suggested...
<tcb:ThumbnailCheckBox ImagePath="{Binding ImagePath}"
IsChecked="{Binding Selected}"
Height="{Binding Path=DataContext.ThumbnailSize, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBox}}}"
CornerRadius="8"
Margin="10">
My only concern is, now the style is accessing the ViewModel for that control directly rather than receiving a bound value.
Suppose I wanted to use the ListBox again, but on another UserControl whose ViewModel didn't have ThumbnailSize property, but used one by another name?
You see where I'm going with this... the current solution is not very extensible and is limited to the current classes as they are named exactly.
In fact, in a perfect world, I'd like to have variable names for the ImagePath and Selected properties, but that's a different discussion.
It's possible to use FindAncestor. The idea of that is, child traverses through logical tree, and tries to find parent with concrete type (in this case, ListBox), and then accesses attached property. See http://wpftutorial.net/BindingExpressions.html for more binding expressions.
In your ItemTemplate, this is how you could access ThumbnailSize property:
{Binding Path=(util:MyAttachedProperties.ImageSize),
RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type ListBox}}}
Essentially, the question asked here was a little bit opposite, but results are same. "How could items in ListBox access ListBox (attached) properties.
Problem
I have a UserControl which contains a ToggleButton and a ComboBox. The control will allow the user to choose a sort type (via ComboBox) and a Direction (via ToggleButton). I want to be able to expose some properties of the ComboBox and more, so how do I bind the ItemsSource of the ComboBox to an Items Property of the UserControl, which I will implement myself, but also the built-in Content property---similar to how a ComboBox can do both.
UserControl
I have a user control which set-up is similar as the below code, or look here.
<UserControl x:Class="Example.DirComboBox">
<Grid>
<ComboBox x:Name="cbItems" />
<ToggleButton x:Name="tbSortDir"/>
</Grid>
</UserControl>
Control Usage
I would like to be able to use it in two ways:
1:
Adding Child Elements.
<local:DirComboBox>
<ComboboxItem Content="Item 1"/>
</local:DirComboBox>
2:
Binding Items Property.
<local:DirComboBox Items="{Binding SortList}"/>
Alternatives
I would be willing to use alternatives, such as setting the root as ComboBox instead of UserControl but I need to expose the follow (but not sure how):
Have a ToggleButton on the side,
SortDirection property as a bool
RoutedEvent for Ascending and Descending
Define Dependancy Properties for SortDirection, Items in your usercontrol. Once you have these properties in your control you can directly set them from outside like:
<local:DirComboBox Items="{Binding SortList}" SortDirection="{Binding Sort}"/>
then inside your control bind these properties to respective controls like:
<UserControl x:Class="Example.DirComboBox">
<Grid>
<ComboBox x:Name="cbItems" ItemsSource="{Binding Items, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}"}/>
<ToggleButton x:Name="tbSortDir" IsPressed="{Binding SortDirection, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}"/>
</Grid>
</UserControl>
keep the binding mode as twoway.
Instead of basing my control on UserControl I changed to using a CustomControl as per the Tutorial on MSDN.
I have a Silverlight application that displays a list of items in a ListBox. Each item represents a different 'page' of my application so I have a style that is applied to the ItemContainerStyle property that looks like this:
<Style x:Key="navigationItemContainerStyle" TargetType="ListBoxItem">
<Setter Property="Margin" Value="5,3"/>
<Setter Property="FontSize" Value="16"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid Cursor="Hand">
<VisualStateManager.VisualStateGroups>
<!-- code omitted --!>
</VisualStateManager.VisualStateGroups>
<Border x:Name="contentBorder"
Background="{StaticResource navigationHighlightBrush}"
CornerRadius="3"
Opacity="0"/>
<ContentControl x:Name="content"
Margin="10,5"
Content="{Binding}"
Foreground="DarkGray"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The style is very simple. It simply displays the Border when the ListBoxItem's visual state is equal to 'Selected'. Notice that the content is hosted by a ContentControl as I want to be able to change the Foreground property when the item is in the 'Selected' state.
This works brilliantly as can be seen by the screenshot below:
Now I want the selected item to invoke navigation so the idea I had was to create a DataTemplate that sets the content of each item to a HyperLinkButton:
<DataTemplate x:Key="navigationListBoxItemTemplate">
<HyperlinkButton Content="{Binding}"
Background="Transparent"/>
</DataTemplate>
Now this doesn't work as the ItemTemplate hosts it's content in a ContentControl rather than a ContentPresenter so I have had to update the ListBoxItem template to use a ContentPresenter instead.
<ContentPresenter x:Name="content" Margin="10,5"/>
I now get the following result:
When I click on the HyperLinkButton the ListBoxItem is now no longer selected (I can click just outside the HyperLinkButton and the ListBoxItem becomes selected). What I really want is for the ListBoxItem to become selected when the HyperLinkButton is clicked. The HyperLinkButton has no concept of selection so I cannot bind to the ListBoxItem's IsSelected property.
Note: The actual navigation works perfectly, the problem is purely with the appearance of the ListBoxItems.
So my questions are:
Can I make it so that when the HyperLinkButton is clicked, the ListBoxItem becomes selected like the first image?
I will also need some way of changing the foreground of the HyperLinkButton when it is selected as this is no longer done in the items template due to me swapping the ContentControl fro a ContentPresenter.
Note: I could probably solve this problem by binding the SelectedItem property of the ListBox to my viewModel and handling the navigation there negating the requirement to have a HyperLinkButton hosted by each ListBoxItem but I am interested in knowing if it is possible using styles and templates to achieve my desired result.
Update
I have tried a couple of things to try and resolve this but so far have been unsuccessful. The first thing I tried was applying a new style to the HyperLinkButton control in my DataTemplate which essentially removes all of the default look and feel from the control but my application still behaves in the way described above.
The second thing I tried was setting the IsHitTestVisible property to false. This allows me to click 'through' the HyperLinkbutton and select the ListBoxItem but this means that the navigation is now no longer invoked.
The ListBoxItem is not selected because the Button (whatever type it is) marks the MouseLeftButtonDown event as Handled. Therefore the required event does not bubble up to the parent ListBoxItem.
Evidently, your ListBoxItem is getting focus from the click (I assume that is the border in your final image), so this must happen regardless.
Under the covers, the ListBoxItem will have a standard LeftMouseButtonDown event handler set up to deal with Selection, and it must have a call to AddHandler to deal with setting focus.
You can achieve something similar by adding your own handler for the event, like so:
listboxitem.AddHandler(UIElement.MouseLeftButtonDownEvent, new System.Windows.Input.MouseButtonEventHandler(MyMouseLeftButtonDownEventHandler), true);
The final parameter instructs the handler to handle handled events...
Attaching this handler I leave to you, but using a behavior is probably the most straight forward. You could event derive a type from Button and have it walk up the Visual Tree to find and select the ListBoxItem... The possibilities are endless.
I want to try out a small, custom ValueConverter in a class which after being instantiated (via default constructor, which only calls InitializeComponents() ), is given another DataContext, specifically an instance of a ViewModel.
Using a StaticResource within the Binding does not work at all (yields a NullReferenceException), since the DataContext has since then been changed (is not this anymore).
I've tried putting DataContext = this; before the InitializeComponents call, no change.
Should I be looking into this MarkupExtension gizmo (as described in this article) ?
I've also tried creating an instance of the custom Value Converter within the ViewModel (the current DataContext), doesn't help either.
I can provide additional details at all times. Thank you in advance !
I'm trying to display a ContextMenu within the TextBlock. The ContextMenu contains a sole MenuItem. The header of the MenuItem can be "Settings" for instance. The Children (rendered as MenuItems as well) of the said MenuItem stem from an Enum, hence the ItemsSource on the MenuItem.
Now all is getting displayed nicely, yet I am trying to make one of the Children (e.g. a member of the Enum) to be selected per default, since there is already a default Setting. Further background info can be found in my other question.
Edit :
...
<UserControl.Resources>
<Helpers:DisplayTypeToDefaultValueConverter x:Key="displayTypeConverter" />
</UserControl.Resources>
...
<TextBlock x:Name="InstructionLabel"
TextWrapping="Wrap" Text="{Binding Path=SelectedNodeText}"
Grid.RowSpan="1">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="Settings" Name="SettingsPop"
DataContext="{Binding}"
ItemsSource="{Binding Source={StaticResource DisplayTypeValues}}"
IsCheckable="True"
Click="SettingsType_Click">
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding}"/>
<Setter Property="IsChecked">
<Setter.Value>
<Binding Converter="{StaticResource displayTypeConverter}" />
</Setter.Value>
</Setter>
</Style>
</MenuItem.ItemContainerStyle>
</ContextMenu>
</TextBlock>
I've only now realized that it's the dreaded ContextMenu. That's the problem, isn't it ?
The DataContext inside the ItemContainerStyle is a member of the DisplayTypeValues collection. The only thing in the XAML you posted that will be affected by the DataContext of the UserControl changing is the InstructionLabel's text. Setting DataContext="{Binding}" as you're doing on the MenuItem is also redundant as the value will already be inherited from the parent ContextMenu.
It's not clear from your question or code what you're expecting from the DataContext or what you're trying to do with it.
Just several thoughts:
Are you sure you didn't miss setting binding path in <Binding Converter="{StaticResource displayTypeConverter}" />?
Did you check the StackTrace of the thrown exception and all it's InnerExceptions to see, whether there was something interesting?
Used an easier solution, as highlighted in my other related question.
Thank you for your input !
I have a customcontrol exposing a Dependency property of type ObservableCollection. When i bind this properrty directly as part ofthe control's mark up in hte containing control everythihng works fine
<temp:EnhancedTextBox
CollectionProperty="{Binding Path=MyCollection, Mode=TwoWay}"/>
But when i try to do the binding in the style created for the control it fails,
<Style x:Key="abc2"
TargetType="{x:Type temp:EnhancedTextBox}" >
<Setter Property="CollectionProperty"
Value="{Binding Path=MyCollection, Mode=TwoWay}"/>
</Style>
Please help !!!!!
Thanks
It has to do with your data context for the style. There is no way for the style to know where MyCollection is coming from because although you may have it defined in the same file, the style does not share the data context.
I would also ask the question as to why you are setting the property in the style? The style is not meant for this sort of operation. The style is supposed to control the look of the UI element not provide the functionality.