I am trying to write a UserControl that will allow both single controls in it (like Label), as well as "layout" controls like StackPanel and friends.
I am having trouble doing that. The code I have works for single controls, but not for layout controls. I have a feeling this is an obvious fix, I am new to WPF. Here is the UserControl XAML:
<UserControl <!-- namespaces omitted for brevity -->>
<UserControl.ContentTemplate>
<DataTemplate>
<ContentPresenter Content="{TemplateBinding Content}" />
</DataTemplate>
</UserControl.ContentTemplate>
</UserControl>
When I try to use it like this:
<my:SpecialUserControl>
hello
</my:SpecialUserControl>
It's fine. But when I try to do something like
<my:SpecialUserControl>
<StackPanel>
<!-- stuff -->
</StackPanel>
</my:SpecialUserControl>
I get an error in Visual Studio Intellisense saying
The specified value cannot be assigned to the collection. The following type was expected: UIElement
And when I run the app (it builds), I get this exception at that place in the XAML:
'Add value to collection of type System.Windows.Controls.UIElementCollection threw an exception.' Line number x and line position y.
What can I do to make my UserControl able to accept any type of content?
The ContentPresenter should be a part of the ControlTemplate (<UserControl.Template>) of your control, not inside your ContentTemplate. I think that could be your problem.
Inherit your UserControl from ContentControl. ContentControl already has Content property combined with ContentPresenter inside ControlTemplate.
You can also use microsoft blend to get ContentControls visual tree.
Related
I have textbox with validation error template inside TabItem.
Problem is when I try to switch between tabs, Validation is getting disappeared. I tried to search and found it is a bug and to make validation work fine, need to wrap DxTabItem content inside AdornerDecorator
So Xaml will look like this.
<TabItem>
<AdornerDecorator>
<StackPanel>
<Textbox/>
</StackPanel>
</AdornerDecorator>
</TabItem>
I want to make it in common place so i don't need to do same in all screens.
Could you give me an example which shows how I can do this in one place.
Thanks,
In my WPF app we are using an adorner for displaying validation messages, in the particular case there is a single row grid that has multiple controls some of which have validation. The problem I'm having is that I want to force the width of the error message control to be the same as the grid but can't seem to find a way to reference that grid from the adorner template. Here is a sample of what I tried:
<ControlTemplate x:Key="Local_TopAdornedTemplateWide">
<StackPanel>
<AdornedElementPlaceholder x:Name="adornedElement"/>
<TextBlock MaxWidth="{Binding Path=ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType=Grid}, ElementName=adornedElement}"
TextWrapping="Wrap"
Text="{Binding Converter={StaticResource Local_ValidationErrorMessageConverter}}"
Style="{DynamicResource Error_Text}"
Padding="2 1 0 0"
Visibility="{Binding ElementName=adornedElement, Mode=OneWay, Path=AdornedElement.IsVisible, Converter={StaticResource BooleanToVisibilityConverter}}"
/>
</StackPanel>
</ControlTemplate>
This causes the application to crash with an XamlParseException.
Ideally the solution would not be specific to a grid so that it would get the width of any container type, but for now grid is the only use case.
Edit:
Here is an example of another template we use in the application; this template would not work for my case as it would limit the error to be the width of a single column of the aforementioned grid:
<ControlTemplate x:Key="Local_TopAdornedErrorTemplate">
<StackPanel>
<AdornedElementPlaceholder x:Name="adornedElement"/>
<TextBlock MaxWidth="{Binding ElementName=adornedElement, Path=ActualWidth}"
TextWrapping="Wrap"
Text="{Binding Converter={StaticResource Local_ValidationErrorMessageConverter}}"
Style="{DynamicResource Error_Text}"
Padding="2 1 0 0"
Visibility="{Binding ElementName=adornedElement, Mode=OneWay, Path=AdornedElement.IsVisible, Converter={StaticResource BooleanToVisibilityConverter}}"
/>
</StackPanel>
</ControlTemplate>
Using snoop I captured the following two screenshots (I could not take one of the full stack to prevent posting anything proprietary)
This shot shows the grid I mentioned previously, within this it is the FinancialTextBox item that is being adorned
This shot shows two things, the item selected in blue is the highest ancestor of the grid in the previous shot, the yellow highlight is the Textbox from the content template
With those two it seems to be apparent that (based on information from Contango's answer) the two items aren't not in the same visual tree which would lead me to believe my question is not possible. However the second template I added (which does work) points that at least some visual information from the adorned element lives on in the place holder.
So now my question boils down to a) does this information include the parent of the adorned element and b) how can this be accessed via a binding on a different element?
This ended up being a lot simpler than the path I was trying to go down.
I was doing some reading on the AdornedElementPlaceholder class and came across this entry on MSDN and noticed that the class actually has a property called parent, with that I tried the following binding and it works perfectly:
MaxWidth="{Binding ElementName=adornedElement,
Mode=OneWay,
Path=AdornedElement.Parent.ActualWidth}"
WPF is quite powerful and flexible.
You can bind any property in any XAML tag to any property in any other XAML tag.
For example, you could write a test app that binds the Text property of an input box to the Text property of a label, so as you type something into the text box, the label would change automatically (assuming you use UpdateSourceTrigger=PropertyChanged). This is a direct XAML to XAML binding, with no C# in sight.
Similarly, you could bind the width of your error box to the width of the parent control, whatever that may be.
Google RelativeSource and AncestorType, this is a great link:
http://druss.co/2013/10/wpf-binding-examples/
See if you can grok how the Visual Tree and Logical Tree works in WPF, once you understand that, you will understand more of how binding works.
I'd also recommend using the free tool Snoop to look at the Visual Tree. XAML Spy is excellent, but not free.
Snoop can tell you if there is anything that has a bad binding at runtime (you set the filters up, and it will list all bad bindings).
You can use Snoop to get the full XAML path of your source (the XAML you wrote above), then get the full XAML path of the target (i.e. the ActualWidth of your Grid), then compare them: it may be quickly apparent that one is not the ancestor of the other, as they are on different branches of the visual tree, or that there is some other issue which is preventing a simple walk up the visual tree from working.
If you just want to get something working, as a proof of concept, try naming the target XAML grid using x:Name, and reference it by name instead of AncestorType.
I customized a Listbox to show a Pie-Chart (each Listitem is one slice of the Pie). To do this i used an Itemtemplate which (for now) only consists of a Shape. To make those shapes form a full circle, i calculated start/endangle for each piece and used a custom ItemsPanelTemplate to stack the Items on top of each other.
When I click anywhere near the Pie, the "last" item gets selected since it is located on top of the others. This is quite obvious, but I hoped since the ItemTemplate only contains a Shape, it would inherit the hit-testing from there and not assume that all items are represented by rectangles.
Where am I supposed to include the hittesting? I would like to set IsHitTestVisible="false" to everything inside my ItemTemplate, except for the shape - but since it doesn't actually contain anything except for my shape, i am stuck right now.
Edit:
I tried surrounding my Shape with a Grid with transparent background, on which i did set IsHitTestVisible="false". This still results in selecting the last element on each click while i would've assumed that nothing would be selected. I think i might be confused about how hittesting is supposed to work?
Code:
Since i am new to WPF i might have missed something during the implementation. I added the relevant codeparts of a minimal example:
My Shape-class:
public class PiePiece : Shape
{
protected override Geometry DefiningGeometry
{
get { return GetPieGeometry() }
}
//some DependencyProperties and helper methods.
private Geometry GetPieGeometry()
{
//calculations
}
}
XAML:
<Window [...] xmlns:Runner="clr-namespace:MyNamespace">
<Window.Resources>
<!-- some custom converters -->
<!-- ListBox-Style with the custom ControlTemplate for my Listbox -->
<ItemsPanelTemplate x:Key="ItemPanel">
<Grid/>
</ItemsPanelTemplate>
</Window.Resources>
<ListBox [...] ItemsPanel="{StaticResource ItemPanel}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="{x:Null}" IsHitTestVisible="False">
<Runner:PiePiece IsHitTestVisible="False" [...]>
<!-- Dependency Properties are set via Multibinding here -->
</Runner:PiePiece>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Window>
I finally found the reason why the hittesting did not work as desired:
The default template for the ListBoxItem-Style surrounds the ContentPresenter with a Border with transparent background. All click-events were caught and handled by that border, instead of my ContentPresenter. Writing my own style for the ListBoxItem and setting the Background to {x:null} as suggested by Gaz fixed the problem (as did removing the border, but I added another one by now for further customizations).
I'd like to move an element from one grid into another and have a problem to assign programmatically a template to the new instance. Further, details of my attempt.
For this purpose, I create an instance of the class together with its visual appearance from the template.
Inside the Window tag I declare the namespace:
xlmns:my="clr-namespace:myNameSpace"
I have a template in resources:
<ControlTemplate x:Key="templateX">
<StackPanel>
<Image Source="pic.png" Width="50" Height="50"/>
</StackPanel>
</ControlTemplate>
and place the element into the grid.
<Grid Grid.Row="2">
<StackPanel>
<my:someClass Template="{StaticResource templateX}" MouseMove="_event">
</StackPanel>
</Grid>
Now, I drag the element, the event "_event" fires. If I push a standard element (e.g. Rectangle) through this, I do the following at the end of the drag-n-drop chain of events:
Rectangle new_instance = new Rectangle();
// place for rectangle's form and color
NewPlace.Children.Add(new_instance);
// place for positioning the rectangle in NewPlace canvas
How, can I do the last part with the element of someClass? If I do
someClass new_instance = new someClass();
NewPlace.Children.Add(new_instance);
the template "templateX" isn't assigned to it.
The issue in this case seems to be that you want to combine two things:
an instance of your custom class (new_instance)
a control template available as a XAML resource
You already know how to create the instance of your class and how to add it to the Children list.
How to retrieve the control template (or for that matter, any other object) from a XAML resource has been discussed in other SO questions, e.g.:
How can I access ResourceDictionary in wpf from C# code?
Accessing a resource via codebehind in WPF
This leads to:
ControlTemplate template = (ControlTemplate)this.FindResource("templateX");
Now, the crucial point is that you do not want to add the control template itself to the Children list. The control template is just a set of instructions how to create a UI tree for your control and bind its properties to those of your control, where appropriate.
Instead, you want to configure new_instance to use the control template you retrieved from the resource. You can do that by assigning the control template to the Template property of new_instance:
new_instance.Template = template;
Once new_instance is added to Children, it will be displayed and it will use your custom control template.
I have a UserControl defined in MyUserControl.xaml. If I reference this directly from another control, then it displays correctly. For example:
<Grid>
<MyUserControl/>
<Grid>
works as expected. But if I try to use the UserControl in a DataTemplate it doesn't work. For example, if I have a DataTemplate like
<DataTemplate>
<MyUserControl/>
<DataTemplate>
nothing gets rendered.
What's up?
Does your UserControl have a corresponding .xaml.cs file? This seems to happen when it doesn't.
If you're using Visual Studio, try copying all the XAML from your UserControl, then deleting it from your project, adding a new UserControl (with the same name as before), then pasting your content back into the XAML file. This will ensure that you have the correct .xaml.cs file set up.