I'm making custom control with edit/view state.
I've made 2 dependencyProperties with default styles:
<Setter Property="EditContent">
<Setter.Value>
<TextBox Text="{Binding ElementName=parent, Path=LocalValue}" />
</Setter.Value>
</Setter>
<Setter Property="ViewContent">
<Setter.Value>
<TextBox IsEnabled="False" Text="{Binding ElementName=parent, Path=LocalValue}" />
</Setter.Value>
</Setter>
and then, displaying these Contents depending on IsReadOnly value like this:
<Border Background="Transparent"
MouseLeftButtonDown="UIElement_OnMouseLeftButtonDown"
Visibility="{Binding ElementName=parent,
Path=IsReadOnly,
Converter={StaticResource BooleanToCollapsingVisibilityConverter},
ConverterParameter=true}">
<ContentPresenter Content="{Binding ElementName=parent, Path=ViewContent}" />
</Border>
Problem is, that when my control loads with IsReadOnly = true, Content Property of my ContentPresenter for EditContent is null.
When I'm changing IsReadOnly to false Content of EditContent loads, but my binding does not work (like it's not evaluated).
How to re-evaluate bindings in WPF, or force ContentPresenter to load it's content on created (even if it's invisible)?
P.S. If I navigate to this ContentPresenter in Snoop (or WPF Inspector) when It's invisible - it's empty. When I navigate to it when it's visible - bindings starting to work
Please, have a look at output windows while debugging. you will see errormessage describing the binding problem. wpf rule nr.1: always check output window.
The reason is that your edit / view content has different NameScope, therefore ElementName does not work. However, in your Control you can set NameScope manually, by using something like:
var currentScope = NameScope.GetNameScope(this);
NameScope.SetNameScope((UIElement)this.EditContent, currentScope)
in your case you are using styles and styles has its own namescope, so it won't work. Imagine, that you used the style on multiple pages. What element should be used?
Sometimes you can use Source={x:Reference elementName}, but you cannot use it in direct children of the source the element, because the element does not exist yet, when the {x:Reference } is being resolved
never set content-like properties inside styles. if you applied your style to more than one element, that the same TextBox from ViewContent would be added to visual tree multiple times and that throws an exception. You should use DataTemplates instead of direct content in styles
Related
So I have a series of pages that I want to display for an application I am working on but I want to start by simply displaying a ViewModel with a ContentPresenter within another ViewModel. I can make it work if I use:
<ContentPresenter>
<ContentPresenter.Content>
<connection:ConnectionSelectPage />
</ContentPresenter.Content>
</ContentPresenter>
I want to make it more advanced using Styles because I will need to be able to switch which Viewmodel is displayed based on a DataTrigger. I have come up with this as a start before delving into the DataTriggers and multiple ViewModels which I want to simply perform the exact same function as the above code:
<ContentPresenter>
<ContentPresenter.Resources>
<Style x:Key="ConnectPage">
<Setter Property="ContentPresenter.Content">
<Setter.Value>
<connection:ConnectionSelectPage />
</Setter.Value>
</Setter>
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
This new code does not display anything in the application and I find that confusing because from what I thought I knew about XAML and WPF, these two blocks of code should be identical. Am I missing something?
Just set your content presenters Content binding to the page property in your main view model...
<ContentPresenter Content='{Binding CurrentPage}' />
...where CurrentPage is of type 'object' or, better yet, some base class you're using for all your page view models. Then you just use data templates to dictate how the ContentPresenter should be populated for each of your page types:
<DataTemplate DataType="{x:Type Page1ViewModel}">
<views:Page1UserControl />
</DataTemplate>
<DataTemplate DataType="{x:Type Page2ViewModel}">
<views:Page2UserControl />
</DataTemplate>
... etc ...
So long as the CurrentPage property supports property change notification the child views will automatically change whenever you change its value.
I have the following XAML code for WPF
<ContentControl>
<ContentControl.Resources>
<Style TargetType="selections:EntitySelector">
<Setter
Property="EntitySelectorManager"
Value="{Binding SelectorManager, Mode=OneWay }"/>
</Style>
</ContentControl.Resources>
<ContentControl.Content>
<Binding Path="Editor" />
</ContentControl.Content>
</ContentControl>
Then in code behind in response to some event I have set the Editor propety
this.Editor = element
where element is a control that contains one or more EntitySelector objects. However once the control is instantiated in the visual tree I can see that the binding has not worked.
First I check the SelectorManager property on the DataContext at the level of the ContentControl. This seems in order
Now I go into the ContentControl and see if any of the EntitySelector controls have their EntitySelectorManager properties set.
You can see that there is a binding expression but the result is Null. Why is this?
I have a solution but I don't really like it. Using a dynamic resource that is initialized from codebehind
public WeinCamWindow(WeinCamWorkPiece camViewModel)
{
ViewModel = camViewModel;
InitializeComponent();
DataContext = ViewModel;
this.Resources["EntitySelectionManager"] = ViewModel.SelectorManager;
}
and in the style use a dynamic resource
<Style TargetType="selections:EntitySelector">
<!-- 'EntitySelectionManager' is set in code behind. -->
<Setter Property="EntitySelectorManager"
Value="{DynamicResource EntitySelectionManager}" />
</Style>
The dyamic resource propogates where all the other tricks I tried didn't work.
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.
This is a bit of a weird question:
I have a custom control that inherits from TextBox, and provides "ghost" text - eg it says "Username" in a box until you click inside it, whereupon the "ghost" text disappears, and the user can type in their, in this case, Username.
The "Ghost text" for a control is simply a property in a subclass of TextBox. I then set TextBox.Text to it whenever relevant.
In the Visual Studio WPF XAML preview window (the standard UI design one), I would like to be able to "preview" the "Ghost text" - like when you set the actual text of a textbox, you can see it in the preview, not just when you run the application.
I have tried setting the Text property to the relevant Ghost text in the OnInitialised function, but it doesn't have any effect on the preview.
Where should I be putting code that affects the preview of a control in the designer?
Bonus question: Is there an actual name for what I call "ghost" textboxes? Would be good to know for the future!
Is there an actual name for what I call "ghost" textboxes? Would be god to know for the future!
I have seen this referred to as a "hint" when describing its purpose, or as a "watermark" when describing its appearance. I tend to employ the former, as it describes the function, which is more in line with the WPF design philosophy: the actual presentation is determined by the template, and the conceptual "hint" could be presented differently simply by applying a custom style/template. Why imply that it should be a watermark when someone could choose to present it in another way?
Design-wise, I think you're approaching this the wrong way. I would implement this such a way that controls other than a TextBox could more easily opt in: use attached properties.
I would create a static class, say HintProperties, which declares a couple of attached dependency properties:
Hint - declares the hint content; typically a string, but it need not be. It could simply be an object, akin to the Content property of a ContentControl.
HasHint - a computed, read-only bool property that gets reevaluated when Hint changes, and simply indicates whether a control has a Hint specified. Useful as a Trigger condition to toggle the visibility of a hint presenter in your control template.
Then, provide a custom style for your TextBox (or other control) which overlays a Hint presenter atop the regular content, hidden by default. Add a trigger to reduce the opacity of the hint when the control has keyboard focus, and another to make the hint Visible when Text is an empty string.
If you really want to go all-out, you can throw in HintTemplate and HintTemplateSelector properties.
However, if this seems like overkill, you can simply declare a Hint or Watermark property directly on your derived TextBox class. I would not try to implement this by conditionally changing the Text property, as that would interfere with data binding and, potentially, value precedence.
You can do this in a reusable way using a style which you would typically declare in your App.xaml. In this style you replace the control template with your own implementation and wrap together some controls. Basically you make up the WatermarkTextBox from a normal TextBox with a transparent background and place a TextBlock control with standard text behind the TextBox. The Visibility of this TextBlock is bound to the TextBox using a specific TextInputToVisibilityConverter so it will disappear when the TextBox has text or just has the focus.
While this maybe looks like a lot of code, you define this once and you can reuse this whereever you need, just by setting style of the TextBox
Declaration of some resources
xmlns:c="clr-namespace:YourNameSpace.Converters"
<SolidColorBrush x:Key="brushWatermarkBackground" Color="White" />
<SolidColorBrush x:Key="brushWatermarkForeground" Color="LightSteelBlue" />
<c:TextInputToVisibilityConverter x:Key="TextInputToVisibilityConverter" />
Declaration of the style:
<Style x:Key="SearchTextBox" TargetType="{x:Type TextBox}">
<Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Grid Background="{StaticResource brushWatermarkBackground}">
<TextBlock Margin="5,5" Text="Search..."
Foreground="{StaticResource brushWatermarkForeground}" >
<TextBlock.Visibility>
<MultiBinding
Converter="{StaticResource TextInputToVisibilityConverter}">
<Binding RelativeSource="{RelativeSource
Mode=FindAncestor, AncestorType=TextBox}"
Path="Text.IsEmpty" />
<Binding RelativeSource="{RelativeSource
Mode=FindAncestor, AncestorType=TextBox}"
Path="IsFocused" />
</MultiBinding>
</TextBlock.Visibility>
</TextBlock>
<Border x:Name="Border" Background="Transparent"
BorderBrush="{DynamicResource SolidBorderBrush}"
BorderThickness="1" Padding="2" CornerRadius="2">
<!-- The implementation places the Content into the
ScrollViewer. It must be named PART_ContentHost
for the control to function -->
<ScrollViewer Margin="0" x:Name="PART_ContentHost"
Style="{DynamicResource SimpleScrollViewer}"
Background="Transparent"/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The implementation of the TextInputToVisibilityConverter, which just takes text input, converts to bool and converts this to Visibility. Also keeps Focus into account.
namespace YourNameSpace
{
public class TextInputToVisibilityConverter : IMultiValueConverter
{
public object Convert(object[] values,
Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (values[0] is bool && values[1] is bool)
{
bool hasText = !(bool)values[0];
bool hasFocus = (bool)values[1];
if (hasFocus || hasText)
return Visibility.Collapsed;
}
return Visibility.Visible;
}
public object[] ConvertBack(object value,
Type[] targetTypes, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
Now all infrastructure is into place. In your view/usercontrol/window just alter the style of the Textbox and there it is, your watermark textbox..
<TextBox Style="{DynamicResource SearchTextBox}" />
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.