Windows 8 Style Apps (ex. "Metro"), Visual Studio 2012, XAML.
I have a UserControl derived from Canvas. It has one child element - a Polygon with its Data bound to a property (with INotifyPropertyChanged implemented):
<Canvas x:Name="MyPolygon">
<Polygon Points="{Binding ElementName=MyPolygon,Path=MyPoints}" ... />
</Canvas>
The property is set and the Polygon is correctly rendered, both at design-time and run-time, if I instantiate that control elsewhere in XAML, passing in a string:
<local:MyPolygon MyPoints="..." />
However, changing the values in that string is tedious. A designer would prefer to have a collection of some UI knots (like Ellipses) visible at design-time but invisible at run-time, so that they could drag them in the designer and have the Polygon reconstruct its geometry on the fly:
<local:MyPolygon>
<Ellipse Canvas.Left="204" Canvas.Top="57" ... />
<Ellipse Canvas.Left="166" Canvas.Top="30" ... />
...
</local:MyPolygon>
Basically I want to keep the geometry information in (extended) .Children. Is this possible?
(There could be some event/constructor where the control could examine its .Children (after those Ellipses are inserted), retrieve their coordinates, and build MyPoints. The designer would have to trigger that event for the geometry to be visible at design time)
Have you looked at design data like this.
if (Windows.ApplicationModel.DesignMode.DesignModeEnabled)
{
GetSampleData();
}
else GetRealData();
or
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
<CollectionViewSource
x:Name="groupedItemsViewSource"
Source="{Binding Groups}"
IsSourceGrouped="true"
ItemsPath="Items"
d:Source="{Binding ItemGroups,
Source={d:DesignInstance Type=data:SampleDataSource,
IsDesignTimeCreatable=True}}"/>
So, I ended up creating a Polygon on the same level where I have the Ellipses.
<Polygon Points="{Binding ElementName=MyPoints,Converter={StaticResource PolygonConverter}}" ... />
<Canvas x:Name="MyPoins">
<Ellipse Canvas.Left="228" Canvas.Top="69" ... />
<Ellipse Canvas.Left="166" Canvas.Top="30" ... />
...
</Canvas>
The binding converter converts coordinates of all .Children of the object to a string.
This works at both design time and run time.
Unfortunately, one must rebuild the project after moving the Ellipses around in order for VS designer to refresh the view and pick up the changes, which makes the design process much less intuitive than it could have been. :/
Related
Brief
I am trying to programmatically change the colour of specific elements at runtime. The project currently uses Telerik and I am able to change the theme at runtime: This works as expected with no issues. I can't, however, figure out how to change the fill or stroke colour at runtime of custom shape elements in XAML.
Within my project I have a ResourceDictionary file named _Icons.xaml that contains vector shapes to use as the content for other controls (such as buttons).
Code
App.xaml.cs
I am using the following code to change the theme's marker colours at runtime.
GreenPalette.Palette.MarkerColor = (Color)ColorConverter.ConvertFromString("#FF000000");
_Icons.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyNamespace">
<ControlTemplate x:Key="Box">
<Viewbox>
<Rectangle Width="357" Height="357" Fill="#000000"/>
</Viewbox>
</ControlTemplate>
<ControlTemplate x:Key="BoxOutline">
<Viewbox>
<Rectangle Width="357" Height="357" StrokeThickness="45" Stroke="#000000"/>
</Viewbox>
</ControlTemplate>
</ResourceDictionary>
MainWindow.xaml
<telerik:RadButton>
<StackPanel>
<ContentControl Template="{StaticResource Box}" Height="58"/>
<TextBlock HorizontalAlignment="Center" Margin="0,5,0,0">Box</TextBlock>
</StackPanel>
</telerik:RadButton>
<telerik:RadButton>
<StackPanel>
<ContentControl Template="{StaticResource BoxOutline}" Height="58"/>
<TextBlock HorizontalAlignment="Center" Margin="0,5,0,0">BoxOutline</TextBlock>
</StackPanel>
</telerik:RadButton>
Question
In _Icons.xaml I have the following lines:
<Rectangle Width="357" Height="357" Fill="#000000"/>
<Rectangle Width="357" Height="357" StrokeThickness="45" Stroke="#000000"/>
Given the following line in App.xaml.cs:
GreenPalette.Palette.MarkerColor = (Color)ColorConverter.ConvertFromString("#FF000000");
How can I either...
Programmatically change the values of Fill and/or Stroke (an element that only has Fill set should only change the Fill value and not add a Stroke attribute) from the App.xaml.cs file? Or ...
Bind the values in XAML for Fill or Stroke to receive the value given by my App.xaml.cs file?
Thank you for taking the time to read my question. Any help regarding this is greatly appreciated.
First i advise you to eject that controls off your resource sheet so you can actually control them properly.
When you do that, go the code behind your control and just use dependency property of type 'Color' of the 'SolidColorBrush' that is used by the background and then bind it by element name, you gotta build the project at least once before attempting to bind.
Here is how you write a dependency property
hint: in VS write 'propdp' and hit tab twice to bring up a template, but you can use mine for now.
public Color _color
{
get { return (Color)GetValue(ColorProperty); }
set { SetValue(ColorProperty, value); }
}
public static readonly DependencyProperty ColorProperty =
DependencyProperty.Register("_color", typeof(Color), typeof(Fileentity), null);
after you build once go to the xalm and put this inside your rectangle:
<Grid.Background>
<SolidColorBrush Color="{Binding
_color,ElementName=YourControlName" />
</Grid.Background>
if you do it right you will be able to access this property when inserting the control on you Page like
<local:YourcontrolName _color="{x:Bind MyColorProperty }"/>
where 'MyColorProperty' is a property that implements INotifyPropertyChanged.
An alternative way is to use a datacontext directly on the usercontrol and just bind your color to one of its properties like:
public YourControl(){
this.InitializeComponent();
this.DataContext = new MyClassDataContext();
var myContext= (MyClassDataContext)this.DataContext;
_color=MyContext.MyColorProperty;}
Where MyClassDataContext is any given class that contains a Color property(MyColorProperty) of your choosing.
You need a Dependency property here as well that binds to your Controls xalm like i showed before.
I know all this is might too hard to grasp at once, thats cause it requires basic knowledge of MvvM.
I have been reading some tutorials on XAML but it does not help me. I have an empty application window and I need to create 30 TextBoxes in 3 rows.
Being used on the win forms, I thought I would figure it out - well, I did not. I cannot seem to find a way how to create them on certain coordinates.
You first want to place a Canvas control on your screen, then you can populate it with TextBoxes placed at whatever Canvas.Left and Canvas.Top position you want.
That said though, WPF has a much better layout/arrangement system than WinForms, and trying to use it like it's WinForms means you'll miss out on a lot of what makes WPF so great, and you'll be making things a lot harder on yourself.
The WPF way of doing the same thing would be to use an ItemsControl, and a collection of objects that each contain data that the UI needs to to know for display purposes.
First you would create a class to represent each TextBox
public class MyClass
{
public string Text { get; set; }
public int X { get; set; }
public int Y { get; set; }
}
Note: This class should implement INotifyPropertyChanged if you want to change the properties at runtime and have the UI automatically update.
Then make a list of this class, and bind it to an ItemsControl
<ItemsControl ItemsSource="{Binding ListOfMyClass}" />
Then you'd want to overwrite the ItemsPanelTemplate to be a Canvas (the best WPF panel for positioning items according to an X,Y position)
<ItemsControl ItemsSource="{Binding ListOfMyClass}">
<!-- ItemsPanelTemplate -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Next overwrite the ItemTemplate to draw each item using a TextBlock
<!-- ItemTemplate -->
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Text}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
And add an ItemContainerStyle that binds Canvas.Left and Canvas.Top properties to X,Y properties on your object
<!-- ItemContainerStyle -->
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding X}" />
<Setter Property="Canvas.Top" Value="{Binding Y}" />
</Style>
</ItemsControl.ItemContainerStyle>
And this will take a List of MyClass objects, and render them to the screen inside a Canvas, with each item positioned at the specified X,Y coordinates.
With all that being said, are you sure this is what you want? WPF has much better layout panels than WinForms, and you don't have to position every element according to an X,Y coordinate if you don't want to.
For a quick visual introduction of WPF's Layouts, I'd recommend this link : WPF Layouts - A Visual Quick Start
Also since it sounds like you're new to WPF and coming from a WinForms background, you may find this answer to a related question useful : Transitioning from Windows Forms to WPF
WPF layout involves choosing a layout container and placing your controls in it. There are several different containers:
The Grid container is a powerful tool for laying out your form in rows and columns. You have complete control over the size of each cell, and you can have rows or columns "span" each other.
The DockPanel container allows you to "dock" controls to the edges of your window or the center. You'd use it to layout a window with smart icon bars, ribbons, status windows, and toolboxes, like Visual Studio itself.
The StackPanel container can be used to stack controls either on top of each other or next to each other
The UniformGrid container is a less powerful version of the container that keeps all cells the same size.
The Canvas container allows you to specify the X & Y coordinates of your controls.
There are one or two others but these are the ones I've used.
The bad thing about laying out a form using X & Y coordinates is that the form does not handle resizing well. This can be exacerbated when you support globalization, as the labels and such for a string may be a lot longer in a foreign language. The best example off the top of my head is Spanish. A lot of English phrases, when translated to Spanish, are a lot longer.
The Grid container gives you the most control over layout. Columns can automatically size themselves to the longest string in the column, while the rest of the columns adjust themselves as necessary, again automatically. You don't have to write one line of code to get that effect; it's all there in the Grid control out of the box.
If you insist on laying out your form the Winforms way, use a Canvas. But you're not going to get the benefit of using the more advanced layout facilities in the other containers, especially the Grid control. I use that almost exclusively in my forms.
EDIT
Using layout controls other than Canvas means that you think about layout differently in WPF than in WinForms. You work at a higher conceptual level and leave the details about figuring out where on the screen a particular control will be displayed to WPF. You also don't have things like the WinForms Anchor property in WPF, which always seemed kind of a hack to me.
The WPF was designed to offer a power and rich framework for designer which make it a different from the classic winforms. You can achieve what want by adding your TextBox control to a canvas and changing the attached property following is a full example illustrating this:
MainWindow
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Canvas Name="mainCanvas" Margin="31,-10,-31,10">
<TextBox Name="myTextBox" Canvas.Left="131" Canvas.Top="109" Height="84" Width="135"></TextBox>
<Button Content="Button" Height="62" Canvas.Left="271" Canvas.Top="69" Width="91" Click="Button_Click"/>
</Canvas>
</Grid>
</Window>
Code Behind
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
myTextBox.SetValue(Canvas.LeftProperty,(double)myTextBox.GetValue(Canvas.LeftProperty)+50.0);
}
}
}
If you want to position the TextBoxes in a grid-way, use Grid:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
...
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Grid.Column="0" />
<TextBox Grid.Row="0" Grid.Column="1" />
<TextBox Grid.Row="0" Grid.Column="2" />
...
<TextBox Grid.Row="1" Grid.Column="0" />
<TextBox Grid.Row="1" Grid.Column="1" />
<TextBox Grid.Row="1" Grid.Column="2" />
...
</Grid>
I'm building a C# WPF application and in one of my XAML Views I need to connect two controls that reside in different containers using a line.
Here is my simplified layout pseudo-code:
<DockPanel>
<Grid DockPanel.Dock="Top">
<Button Name="Button1" />
</Grid>
<UniformGrid Columns="3" DockPanel.Dock="Bottom">
<StackPanel>
<Button Name="ButtonA" />
</StackPanel>
<StackPanel>
<Button Name="ButtonB" />
</StackPanel>
<StackPanel>
<Button Name="ButtonC" />
</StackPanel>
</UniformGrid>
</DockPanel>
My requirement is to connect Button1 to ButtonA, B or C through a Line but I can't figure out how. From what I've researched normally people use Canvas and connect the controls hosted in that Canvas and use the attached properties Canvas.SetTop and Canvas.SetLeft to position the controls inside the container. I tried wrapping my DockPanel with a Canvas but that didn't work out.
My question is: Is it possible to draw lines that connect controls across different types of layouts? (in my case DockPanel-Grid-UniformGrid) or what is an alternative or more standard way of achieving this. I also tried getting my control positions relative to the DockPanel but didn't work either...
Thanks in Advance
Based on Charles Petzold 'Thinking outside the grid' article I ended up with a structure like this:
<UserControl ... >
<Grid ... >
<local:SimpleUniformGrid ... >
<Button ... />
<Button ... />
...
</local:SimpleUniformGrid>
<Canvas>
<Path ... />
<Path ... />
<Path ... />
</Canvas>
</Grid>
</UserControl>
The key paragraph is this one:
"When processing the LayoutUpdated event, you don’t want to do anything that will cause another layout pass and get you embroiled in an infinite recursion. That’s where the Canvas comes in handy: Because it always reports a zero size to its parent, you can alter elements in the Canvas without affecting layout."
In addition to that I used Denis Vuyka MyThumb class from his Dragable Objects article
The starting and ending points when creating lines can be determined using the GeneralTransform class as explained in this article:
GeneralTransform transform = thumb.TransformToAncestor(this);
Point rootPoint = transform.Transform(new Point(0, 0));
The key lesson here was understanding that Canvas does not occupy any space inside of a Grid, so you can use it as a container for storing your Paths/LineGeometry and display them at any any coordinate in your view no matter if source/target controls belong to different containers.
I am trying to learn WPF.. Although, Im having trouble with the layouts and which one to choose. I dont want to use canvas because the whole point is the get the hang of WPF..
I have decided to transfer one of my simple programs (in Windows Forms) to WPF..
I have attached the picture of the simple, 1 page form.. Can someone suggest how I could replicate this in WPF?
Form layouts are an interesting predicament. They usually involve a LOT of boilerplate, there's many techniques for removing boilerplate code in form layouts but they're fairly advanced WPF Concepts.
The Simplest Solution for you is going to be a StackPanel for laying out your sections and putting a Grid inside your GroupBox controls.
The Grid can be setup with 4 colunms:
Col 1 Label
Col 1 Body
Col 2 Label
Col 2 Body
With a global style in the resources of your stack panel you can define default visual behaviour so the items dont end up touching:
<Style TargetType="TextBox">
<Setter Property="Margin" "0,0,5,5" />
</Style>
The Above Style will put a 5px margin on the right & bottom of all TextBox controls under it in the visual tree.
This is the absolute simplest (read: straight forward) approach to making this ui in WPF. It is by no means the best, or the most maintainable, but it should be doable in about 10 minutes max.
There are other methods out there for emulating a form layout with WPF like this one or by using other combinations of basic layout components.
For example:
<StackPanel>
<!-- Vertical Stack Panel, Stacks Elements on top of each other -->
<StackPanel Orientation="Horizontal">
<!-- Horizontal Stack Panel, Stacks Elements left to right -->
<Label Width="100">This Label is 100units Wide</Label>
<TextBox />
</StackPanel>
</StackPanel>
Different approaches have different drawbacks, some are flex width, some are not, some play nicely with colunms, some don't. I'd suggest experimenting with the many subclasses of Panel to see what they all do, or you can even roll your own.
Using Grid as container, TextBlock al read-only text, Textbox as editable text and Button.
With these elements and using (for example) XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="MainWindow"
Width="640" Height="480" Background="White">
<Grid>
<TextBlock HorizontalAlignment="Left" Height="20" Margin="34,30,0,0"
TextWrapping="Wrap" Text="Connection String" VerticalAlignment="Top"
Width="107" Foreground="Black"/>
<TextBox HorizontalAlignment="Left" Height="18" Margin="170,32,0,0"
TextWrapping="Wrap" VerticalAlignment="Top" Width="379"/>
<Button Content="Save" HorizontalAlignment="Left" Height="26"
Margin="529,387,0,0" VerticalAlignment="Top" Width="69"/>
</Grid>
you can put all objects in your Window. But if you prefer you can add the elements programmatically. This is the result:
Here an introduction to WPF layout.
I'm having trouble setting up nested styles in WPF. I don't know if I'm doing things the 'right way' - but I'll describe what I'm attempting to do and provide some code.
I'm working with Kinect and am using a ContentControl to represent a Kinect object in my ViewModel. I declare it as such:
<ContentControl Content="{Binding Kinect}" ContentTemplate="{StaticResource SkeletonTemplate}" />
I then set up the SkeletonTemplate as such:
<DataTemplate x:Key="SkeletonTemplate">
<Grid>
<ContentControl Content="{Binding HandLeft}" ContentTemplate="{StaticResource JointTemplate}"/>
<ContentControl Content="{Binding HandRight}" ContentTemplate="{StaticResource JointTemplate}"/>
</Grid>
</DataTemplate>
Lastly, I set up a JointTemplate as such:
<DataTemplate x:Key="JointTemplate">
<Ellipse Fill="Red" Margin="0,0,620,460">
<Ellipse.RenderTransform>
<TranslateTransform X="{Binding Path=Position.X}" Y="{Binding Path=Position.Y}" />
</Ellipse.RenderTransform>
</Ellipse>
</DataTemplate>
Everything is all hunky dory except I'd really like to be able to set a Style at highest level that allows me to style the nested elements. For example, I want to be able to do this:
<ContentControl Content="{Binding Kinect}" Style="{DynamicResource ShrunkBlueSkeleton}" ContentTemplate="{StaticResource SkeletonTemplate}" />
And immediately apply a set of rules to the nested elements. Make the ellipses Blue, apply a ValueConverter to the ContentControls in the SkeletonTemplate to scale them to a smaller part of the screen, etc.
I'm having a bitch of a time getting it working, and I'm not sure if I've even set up everything up in the 'right' way to this point.
Certainly I could re-declare a ton of different ContentTemplates that style in all the different ways I need to, but that is much more obviously bad style.
Any pros out there that can lend a hand?
I could paste the Style attempts I've made thus far, but I'm rather convinced they won't help the discussion.
The root feature that you are missing is that Style's setters works on Properties and not on Path (a la Binding) - which I believe is what you would like to have.
There are technical reasons for Microsoft did that: Style works on specific styles and not on compound elements. Similar styling frameworks (most notably: CSS) also support "setters" just for one element (although, arguably, the select is much superior).