I have a UWP UserControl that contains a path element, the Data property of the path is bound to a string property of the UserControl called Icon. When I add my control to a page and set it's Icon property to a resource item, the control doesn't render the icon and has 0 width in the designer. When I deploy the application to my device the control renders as expected. Is there any way to fix this?
For reference, I'm trying to build a simple toolbar that has a bunch of clickable icons. I'm sure there are other ways of achieving this but I'm using this as a learning as my XAML skills are pretty lacking. My code can be found below.
MainPage.xaml
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Stretch">
<local:ActionIcon IconData="{StaticResource Test}" ></local:ActionIcon>
</StackPanel>
ActionIcon.xaml
<UserControl x:Name="userControl"
x:Class="UwpTest.ActionIcon"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UwpTest"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="200"
d:DesignWidth="200">
<Viewbox Stretch="UniformToFill">
<Path Stretch="UniformToFill"
Data="{Binding IconData, ElementName=userControl}"
Fill="Black" />
</Viewbox>
</UserControl>
ActionIcon.xaml.cs
public sealed partial class ActionIcon : UserControl
{
public ActionIcon()
{
InitializeComponent();
}
public string IconData
{
get
{
return (string) GetValue(IconDataProperty);
}
set
{
SetValue(IconDataProperty, value);
}
}
public static readonly DependencyProperty IconDataProperty = DependencyProperty.Register(
"IconData", typeof(string), typeof(ActionIcon), new PropertyMetadata(default(string)));
}
ResourceDictionary Entry
<x:String x:Key="Test">M10,16.5V7.5L16,12M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z</x:String>
In a nutshell, the problem is that the types of IconData and Path.Data don't match. While IconData is a string, Path.Data wants a Geometry. So if you want to put the path's data in a resource dictionary, its type has to be Geometry or one of its subclasses. Consider, that WPF won't throw an exception when a binding fails. Instead you get a message in the Output-Window of Visual Studio.
But why can I use a string when I set the Path.Data-Property directly?
The Path never really gets a string. When the XAML parser gets a wrong type for a property, it looks at the class of the type it expected. It's searching there for a TypeConversion-Attribute. When the parser looks at Geometry, it finds such an attribute:
[TypeConverter(typeof(GeometryConverter))]
abstract partial class Geometry
The attribute specifies a TypeConverter. For Geometry it is GeomtryConverter, which can convert strings to Geometry. There's an article at msdn about that, if you want to know more: How to: Implement a Type Converter
Fine, but how do I use Geomtries in ResourceDictionaries?
The thing becomes clear once you try to create an path without asiging a string to Path.Data. A simple bezier curve before:
<Path Data="M 10,100 C 10,300 300,-200 300,100" />
and after:
<Path>
<Path.Data>
<PathGeometry>
<PathFigure StartPoint="10,100">
<BezierSegment Point1="10,300" Point2="300,-200" Point3="300,100"/>
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>
The TypeConverter produces such an output. Only the points are again converted by a TypeConverter. However, the next step is simple: Just put the PathGeometry in the ResourceDictionary.
As a little side node: You can avoid using ElementName for every binding by setting the user controls DataContext to itself:
<UserControl DataContext={Binding RelativeSource={RelativeSource Self}}" />
Related
DoubleToVisibilityConverter can be used to easily change a double value to a Visibility based one based on a given threshold value. If both GreaterThan and LessThan are set, the converter will set the visibility if the target value is in-between those two values. Otherwise, it will look for the target being greater than or less than the specified value.
so my question is ----
How to use DoubleToVisibilityConverter for scrollviewer as i want to hide (back to Top)Button and scrollbar is at last down position and when scrollbar is going to up it will show the button.
i am using back to top button for scroll to top.
[ same as working in many website (scrollUp Button) ]
Below one is MainPage.xaml
XAML - Page Resource
<Page.Resources>
<converters:DoubleToVisibilityConverter x:Key="GreaterThanToleranceVisibilityConverter" GreaterThan="65.0"/>
</Page.Resources>
XAML - Ui Element
<Button x:name="Scroll_To_Up_Button" Visibility="{Binding ScrollableHeight, Converter{StaticResourceGreaterThanToleranceVisibilityConverter}/>
where i have to add
ScrollableHeight
in MainPage.xaml.cs and how to use in MyUWP App.
Did you try adding a parameter eg.
<Button x:name="Scroll_To_Up_Button" Visibility="{Binding ScrollableHeight, Converter={StaticResource GreaterThanToleranceVisibilityConverter}, ConverterParameter=42/>
Edit:
Give your page a name:
<Page Name="thisPage"
... >
Then point your binding source to it to access the dependency property ScrollableHeight if that is the type you declared it:
<Button x:name="Scroll_To_Up_Button" Visibility="{Binding ScrollableHeight, ElementName=thisPage, Converter={StaticResource GreaterThanToleranceVisibilityConverter}, ConverterParameter=42/>
And the dependency property in MainPage.xaml.cs:
public static readonly DependencyProperty ScrollableHeightProperty = DependencyProperty.Register(nameof(ScrollableHeight), typeof(int), typeof(*NAMEOFYOURPAGE*), new PropertyMetadata(null));
public int ScrollableHeight
{
get => (int)GetValue(ScrollableHeightProperty);
set => SetValue(ScrollableHeightProperty, value);
}
There is no need to write code in MainPage.xaml.cs for the binding of DoubleToVisibilityConverter. You could set the GreaterThan property or LessThan property of a DoubleToVisibilityConverter instance in MainPage.xaml file. And you should bind the ScrollViewer.VerticalOffset property which gets the distance the content has been scrolled vertically instead of ScrollViewer.ScrollableHeight property which is a constant if the content of the ScrollViewer is determined.
You could refer to the document for the usage of DoubleToVisibilityConverter.
Please check the following code as a sample:
<Page.Resources>
<converters:DoubleToVisibilityConverter x:Key="GreaterThanToleranceVisibilityConverter"
GreaterThan="100" TrueValue="Visible" FalseValue="Collapsed"/>
</Page.Resources>
<StackPanel>
<ScrollViewer x:Name="scrollViewer" Width="300" Height="300" VerticalScrollBarVisibility="Visible"
VerticalScrollMode="Enabled">
<TextBlock Text="111122222222222222222222222222222222211111111111111111111111111"
Width="10" TextWrapping="Wrap"
/>
</ScrollViewer>
<Button x:Name="Scroll_To_Up_Button" Content="back to up" Click="Scroll_To_Up_Button_Click" Margin="10"
Visibility="{Binding VerticalOffset,ElementName=scrollViewer,
Converter={StaticResource GreaterThanToleranceVisibilityConverter},FallbackValue=Collapsed,
TargetNullValue=Collapsed
}"/>
</StackPanel>
I would like to create a custom control that provides all the functionality of the DockPanel, but it also exposes a secondary Overlay that is "outside" of the DockPanel. There would be a dependency property that will control the visibility of the the overlay panel, such that when the property is set true/visible, the Panel will appear overlayed on top of everything within the DockPanel.
Ideally the consumer would be able to drop the control into the same situation as a normal DockPanel, and with no other changes it would behave just like the normal DockPanel:
<DockPanelWithOverlay LastChildFill="True" >
<Button DockPanel.Dock="Bottom".../>
<Button DockPanel.Dock="Top".../>
<Grid>
<Grid controls.../>
</Grid>
</DockPanelWithOverlay>
However, there would be available the secondary area into which they could place the additional content and invoke when required.
<DockPanelWithOverlay LastChildFill="True" >
<Button DockPanel.Dock="Bottom".../>
<Button DockPanel.Dock="Top".../>
<Grid>
<Grid controls.../>
</Grid>
<DockPanel.Overlay>
<whatever controls for the overlay>
</DockPanel.Overlay>
</DockPanelWithOverlay>
But that wouldn't be valid since the Content is being set twice? So to cope, when using the overlay I guess I would have to explicitly state what goes where?:
<DockPanelWithOverlay LastChildFill="True" >
<DockPanel.Children>
<Button DockPanel.Dock="Bottom".../>
<Button DockPanel.Dock="Top".../>
<Grid>
<Grid controls.../>
</Grid>
</DockPanel.Children>
<DockPanel.Overlay Visibility="{Binding IsVisible}">
<whatever controls for the overlay>
</DockPanel.Overlay>
</DockPanelWithOverlay>
I'm not entirely sure the best way to tackle this: whether to create a CustomControl, or a UserControl, inherit directly from the DockPanel and try to expose a separate ContentControl, or maybe inherit from Panel and delegate the MeasureOverride and ArrangeOverride to the DockPanel.
How should I tackle this?
Interesting question. I wrote a DockPanelWithOverlay component that does the work:
I chose here the CustomControl because I wanted to have inheritance of Panel.
But Panel doesn't have a template it can change.
So I wrote a Custom Control inheriting of Control with a custom template
But a Usercontrol would perfectly work I think (I didn't try to be honest)
Edit UserControl is not so good, because it inherits of ContentControl.
So it can only have one children.
The goal of the DockPanelWithOverlay is to have many children.
So I think UserControl is not the best inheritance, as often.
UserControl is better when you want to provide some content in xaml, mostly static, not customizable by user of control.
End of edit
To organize content inside the tempalte, I used a Grid.
Order of the two components matters.
It is the drawing order.
Grid allows to put two components at the same place :
Inside there'll be the Overlay control, and a underlying DockPanel.
DockPanelWithOverlay
..|
..|-ControlTemplate
......|
......|-Grid
..........|
..........|--DockPanel
..........|--OverlayControl
Having a template is easier to make some binding from the DockPanelWithOverlay to the template's controls properties.
(To generate a CustomControl, create a WPFCustom Control Library Project)
Excerpt of themes\generic.xaml in library :
<Style TargetType="{x:Type local:DockPanelWithOverlay}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:DockPanelWithOverlay}">
<!-- the grid allows to put two components at the same place -->
<Grid >
<DockPanel x:Name="dockPanel" />
<ContentControl x:Name="overlayControl" Visibility="{TemplateBinding OverlayVisibility}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Inheriting of control allows to use the template to create the small UIElements hierarchy.
Some dependency properties must be added for allowing binding :
Overlay for providing some UIElements, or a string for overlay content
OverlayVisibility for hiding/showing the overlay
Here is the code for the DockPanelWithOverlay :
(Note the OnApplytemplate called just after the templates componenets are called)
// Children is the property that will be valued with the content inside the tag of the control
[ContentProperty("Children")]
public class DockPanelWithOverlay : Control
{
static DockPanelWithOverlay()
{
// Associate the control with its template in themes/generic.xaml
DefaultStyleKeyProperty.OverrideMetadata(typeof(DockPanelWithOverlay), new FrameworkPropertyMetadata(typeof(DockPanelWithOverlay)));
}
public DockPanelWithOverlay()
{
Children = new UIElementCollection(this, this);
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
// once the template is instanciated, the dockPanel and overlayCOntrol can be found from the template
// and the children of DockPanelWithOverlay can be put in the DockPanel
var dockPanel = this.GetTemplateChild("dockPanel") as DockPanel;
if (dockPanel != null)
for (int i = 0; i < Children.Count; )
{
UIElement elt = Children[0];
Children.RemoveAt(0);
dockPanel.Children.Add(elt);
}
}
// Here is the property to show or not the overlay
public Visibility OverlayVisibility
{
get { return (Visibility)GetValue(OverlayVisibilityProperty); }
set { SetValue(OverlayVisibilityProperty, value); }
}
// Here is the overlay. Tipically it could be a Texblock,
// or like in our example a Grid holding a TextBlock so that we could put a semi transparent backround
public Object Overlay
{
get { return (Object)GetValue(OverlayProperty); }
set { SetValue(OverlayProperty, value); }
}
// Using a DependencyProperty as the backing store for OverlayProperty.
// This enables animation, styling, binding, etc...
public static readonly DependencyProperty OverlayProperty =
DependencyProperty.Register("Overlay", typeof(Object), typeof(DockPanelWithOverlay), new PropertyMetadata(null));
public static readonly DependencyProperty OverlayVisibilityProperty =
DependencyProperty.Register("OverlayVisibility", typeof(Visibility), typeof(DockPanelWithOverlay), new PropertyMetadata(Visibility.Visible));
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public UIElementCollection Children
{
get { return (UIElementCollection)GetValue(ChildrenProperty); }
set { SetValue(ChildrenProperty, value); }
}
public static readonly DependencyProperty ChildrenProperty =
DependencyProperty.Register("Children", typeof(UIElementCollection), typeof(DockPanelWithOverlay), new PropertyMetadata(null));
}
Using the DockPanelWithOverlay :
<lib:DockPanelWithOverlay x:Name="dockPanelWithOverlay1"
OverlayVisibility="Visible"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Button Content="Top" Height="50" DockPanel.Dock="Top" Background="Red"/>
<Button Content="Bottom" Height="50" DockPanel.Dock="Bottom" Background="Yellow"/>
<Button Content="Left" Width="50" DockPanel.Dock="Left" Background="Pink"/>
<Button Content="Right" Width="50" DockPanel.Dock="Right" Background="Bisque"/>
<Button Content="Center" Background="Azure"/>
<lib:DockPanelWithOverlay.Overlay>
<Grid Background="#80404080">
<TextBlock Text="Overlay" FontSize="80" Foreground="#FF444444" HorizontalAlignment="Center" VerticalAlignment="Center" RenderTransformOrigin="0.5,0.5">
<TextBlock.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="-15"/>
<TranslateTransform/>
</TransformGroup>
</TextBlock.RenderTransform>
</TextBlock>
</Grid>
</lib:DockPanelWithOverlay.Overlay>
</lib:DockPanelWithOverlay>
The overlay can easily be switched on or off binding from a CheckBox.IsChecked property for instance.
Here is the full working code : http://1drv.ms/1NfCl9z
I think it 's really the answer to your question. Regards
I suggest we should try to clarify how you see this working. My guess is that the secondary panel will be a DockPanel too, and will completely cover the main panel. I.e., you see either the one or the other, but never both. How do you envisage switching between the two? A ToggleButton perhaps? Or only under control of, say, some Trigger?
My first thought as to implementation is that you seem to like how DockPanel? lays things out, so why touch the layout methods? One way might be to have only one dockpanel, but two collections of children, which you set according to which you want to show. Or the secondary panel in a `Popup?
Do you want to be able to write something like this:
<DockPanelWithAlternative
AlternativeVisibility="{Binding somethingHere}" >
<TextBlock Dock.Top ... />
<TextBlock Dock.Alternative.Top ... />
</DockPanelWithAlternative>
What I am thinking of is something like:
<UserControl>
<Grid>
<DockPanel x:Name="MainPanel" ZIndex="0"/>
<DockPanel x:Name="AlternativePanel" Visbility=... ZIndex="1"/>
</Grid>
</UserControl>
I am quite new to WPF and have a basic question :
Assume the following is xaml declaration in my xaml file
<ContentControl x:Name="interactionActivityContent" Loaded="interactionActivityContent_Loaded">
<shapes:BaseShape.DragThumbTemplate >
<ControlTemplate x:Name="interactionActivityTemplate">
<Grid AllowDrop="True" x:Name="GridTest" >
<Rectangle Name="Interaction" Fill="Yellow" Stroke="Green" StrokeThickness="2" IsHitTestVisible="True" AllowDrop="True"></Rectangle>
<local:DesignerCanvas x:Name="ActivitiesCanvasArea" Margin="1,1,1,1" IsHitTestVisible="True" AllowDrop="True" Background="Blue"></local:DesignerCanvas>
</Grid>
</ControlTemplate>
</shapes:BaseShape.DragThumbTemplate>
*shapes:BaseShape.DragThumbTemplate is coming from some different class.
*DesignerCanvas is my own custom canvas for which I want to set value at run time.
How can I access ActivitiesCanvasArea in my C# code from the code behind file?
Do I need to change the way xaml is declared. I need to apply DragThumbTemplate to my grid so that I can move around grid on main screen.
From http://blogs.msdn.com/b/wpfsdk/archive/2007/03/16/how-do-i-programmatically-interact-with-template-generated-elements-part-i.aspx
If you need to find a named element in the ControlTemplate of myButton1, like the Grid, you can use Template.FindName like so:
// Finding the grid generated by the ControlTemplate of the Button
Grid gridInTemplate = (Grid)myButton1.Template.FindName("grid", myButton1);
I'm working on an interactive map. I'm using Silverlight 4 within VisualStudio 2010.
My problem is that I can't assign a geometry to Button Clip property:
Code:
bouton1.Clip = (PathGeometry)Forme.Data;
//forme is a class that inherits from Path
when I run my application I get an ArgumentException:
The value is not included in the expected range
Your Path called "Forme" has its geometry defined using the Path Mini-Language right?
This type of Geometry cannot be share by multiple elements.
The work-around is store the path data as a string in a ResourceDictionary accessible to both your "Forme" element and "bouton1" then assign it using StaticResource. Something like:-
<StackPanel>
<StackPanel.Resources>
<sys:String x:Key="MyPath">M 10,100 C 10,300 300,-200 300,100</sys:String>
</StackPanel.Resources>
<Button x:Name="btn" Content="Button" Height="150" Clip="{StaticResource MyPath}" />
<Path Data="{StaticResource MyPath}" Stroke="Black" StrokeThickness="2" />
</StackPanel>
The painful downside is that the VS2010 designer doesn't grasp this and therefore doesn't apply the path. You would need to run the app to visually see the results.
I changed button with Path and MouseLeftButtonDown event, it works :)
Why the Path and Polyline have different renderings in WPF?
This is happening both in code and blend, maybe a I missing something or this
is just a anti aliasing effect.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="GeometryMonky.Window1"
x:Name="Window"
Title="Window1"
Width="640" Height="480" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
<Grid x:Name="LayoutRoot">
<Path Fill="#FFFFFFFF" Stretch="Fill" Stroke="#FF0000FF" Margin="100,10,0,0" Data="M289,39 L333,173" Width="1" HorizontalAlignment="Left" Height="100" StrokeThickness="1"/>
<Polyline Stroke="#FF0000FF" Margin="115,178,417,168" StrokeThickness="1" Width="100" Height="100">
<Polyline.Points>
<Point>10,0</Point>
<Point>10,100</Point>
</Polyline.Points>
</Polyline>
</Grid>
</Window>
Image sample from Blend:
http://img190.imageshack.us/img190/2965/wpfsmaple.png
Development system:
WinXP SP2, VS 2008 + SP1
It has to do with drawing modes of non-text objects. I tried setting the polyline object like the article linked below says and it does make it look just like the path.
So, short answer is it has to do with anti-aliasing. Here is the article: Single Pixel Lines
If you want the command here it is, give your polyline a Name and then add the following to the code behind.
public partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
// THIS IS THE LINE THATS IMPORTANT
pLine.SetValue(RenderOptions.EdgeModeProperty, EdgeMode.Aliased);
}
}
Your xaml change here:
<Polyline x:Name="pLine" Stroke="#FF0000FF" Margin="115,178,417,168" StrokeThickness="1" Width="100" Height="100">
<Polyline.Points>
<Point>10,0</Point>
<Point>10,100</Point>
</Polyline.Points>
</Polyline>
This will make your polyline object look just like your Path object. However changing the Path to use unspecified does not do anything so you can make your other objects look similar to the path but not vice versa.
Put this in your Path or Polyline xaml tag
RenderOptions.EdgeMode="Aliased"