I am trying to create a WPF app that allows you to create rectangles on the screen, resize the rectangles and move them around, and add new re-sizable and movable rectangles on the click of a button.
I don't know much about WPF, so I have found some code that does resizing of shapes here
This resizing works will, but I am now stuck with how to dynamically create a new version of the control. I am using a ContentControl which handles the resizing, and it has an inner Rectangle for display. The xaml looks like the following:
<Window x:Class="MazeBuilder.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:MazeBuilder"
Title="MainWindow" Height="480" Width="640">
<Window.Resources>
<!-- ResizeDecorator Template -->
<ControlTemplate x:Key="ResizeDecoratorTemplate" TargetType="{x:Type Control}">
<Grid>
<s:ResizeThumb Height="3" Cursor="SizeNS" Margin="0 -4 0 0"
VerticalAlignment="Top" HorizontalAlignment="Stretch"/>
<s:ResizeThumb Width="3" Cursor="SizeWE" Margin="-4 0 0 0"
VerticalAlignment="Stretch" HorizontalAlignment="Left"/>
<s:ResizeThumb Width="3" Cursor="SizeWE" Margin="0 0 -4 0"
VerticalAlignment="Stretch" HorizontalAlignment="Right"/>
<s:ResizeThumb Height="3" Cursor="SizeNS" Margin="0 0 0 -4"
VerticalAlignment="Bottom" HorizontalAlignment="Stretch"/>
<s:ResizeThumb Width="7" Height="7" Cursor="SizeNWSE" Margin="-6 -6 0 0"
VerticalAlignment="Top" HorizontalAlignment="Left"/>
<s:ResizeThumb Width="7" Height="7" Cursor="SizeNESW" Margin="0 -6 -6 0"
VerticalAlignment="Top" HorizontalAlignment="Right"/>
<s:ResizeThumb Width="7" Height="7" Cursor="SizeNESW" Margin="-6 0 0 -6"
VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
<s:ResizeThumb Width="7" Height="7" Cursor="SizeNWSE" Margin="0 0 -6 -6"
VerticalAlignment="Bottom" HorizontalAlignment="Right"/>
</Grid>
</ControlTemplate>
<!-- Designer Item Template-->
<ControlTemplate x:Key="DesignerItemTemplate" TargetType="ContentControl">
<Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
<Control Template="{StaticResource ResizeDecoratorTemplate}"/>
<ContentPresenter Content="{TemplateBinding ContentControl.Content}"/>
</Grid>
</ControlTemplate>
</Window.Resources>
<Canvas x:Name="LayoutRoot" MouseDown="LayoutRoot_MouseDown" MouseMove="LayoutRoot_MouseMove">
<Popup Name="PopupEsales" Placement="Right" IsEnabled="True" IsOpen="False" Grid.RowSpan="2">
<ListView Height="145" HorizontalAlignment="Stretch" Margin="0,0,0,0" Name="lvSalesPersonIdSearch" VerticalAlignment="Top" Width="257" >
<ListView.View>
<GridView>
<GridViewColumn Header="Sales Persons Id" Width="100" DisplayMemberBinding="{Binding Path=SPID}" />
<GridViewColumn Header="Name" Width="100" DisplayMemberBinding="{Binding Path=Name}" />
</GridView>
</ListView.View>
</ListView>
</Popup>
<Menu Height="23" IsMainMenu="True" HorizontalAlignment="Left" Name="menu1" VerticalAlignment="Top" Width="640">
<MenuItem Header="File">
<MenuItem Header="New Maze" Click="New_Click" />
<MenuItem Header="Load Maze" Click="Load_Click" />
<MenuItem Header="Save Maze" Click="Save_Click" />
</MenuItem>
<MenuItem Header="Tools">
<MenuItem Header="Show" Click="ShowTools_Click" />
</MenuItem>
</Menu>
<ContentControl Width="130"
MinWidth="50"
Height="130"
MinHeight="50"
Canvas.Top="150"
Canvas.Left="470"
Template="{StaticResource DesignerItemTemplate}">
<Rectangle Fill="Blue"
IsHitTestVisible="False"/>
</ContentControl>
<Canvas.Background>
<SolidColorBrush Color="White" Opacity="0"/>
</Canvas.Background>
</Canvas>
The control that I am trying to duplicate dynamically is this bit:
<ContentControl Width="130"
MinWidth="50"
Height="130"
MinHeight="50"
Canvas.Top="150"
Canvas.Left="470"
Template="{StaticResource DesignerItemTemplate}">
<Rectangle Fill="Blue"
IsHitTestVisible="False"/>
</ContentControl>
It is the Template property that is giving me the problem - how do I create it dynamically.
What I have tried is to read in the Xaml into A XamlReader to create the control, like so:
private void CopyBlockWithXaml()
{
StringBuilder sb = new StringBuilder();
sb.Append( #"<ContentControl xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'");
sb.Append( " Width=\"130\" MinWidth=\"50\" Height=\"130\"");
sb.Append(" MinHeight=\"50\" ");
sb.Append(" Canvas.Top=\"150\"");
sb.Append(" Canvas.Left=\"470\"");
sb.Append(" Template=\"{StaticResource DesignerItemTemplate}\">");
sb.Append(" <Rectangle Fill=\"Blue\"");
sb.Append(" IsHitTestVisible=\"False\"/>");
sb.Append("</ContentControl>");
ContentControl cc= (ContentControl) XamlReader.Parse(sb.ToString());
LayoutRoot.Children.Add(cc);
}
Exception:
$exception{"'Provide value on 'System.Windows.StaticResourceExtension' threw an exception.' Line number '1' and line position '258'."} System.Exception {System.Windows.Markup.XamlParseException}
Dynamically creating controls by parsing Xaml at run time will work, but it is probably not the easiest approach.
Instead you could create new instances of the content control using regular C# code. Try something like this in your code behind:
var contentControl = new ContentControl
{
Width = 130,
MinWidth = 50,
Height = 130,
MinHeight = 40
};
Canvas.SetLeft(contentControl, 150);
Canvas.SetTop(contentControl, 470);
contentControl.Content = new Rectangle { Fill = new SolidColorBrush(Color.Blue) };
LayoutRoot.Children.Add(contentControl);
Now this code sample probably won't compile as it is (I don't have a compiler here), but you get the idea. Just create the controls in code as you would do any other classes.
This was the code I ended up using (thanks Rune Grimstad) - it was setting the Template property that was the tricky bit for me to figure out
ContentControl cc = new ContentControl();
ControlTemplate ct = new ControlTemplate();
object rs = this.Resources["DesignerItemTemplate"];
ct = (ControlTemplate)rs;
cc.Template = ct;
cc.Height = 10;
cc.Width = 10;
cc.Content = new Rectangle { Fill = new SolidColorBrush(Color.FromRgb(0,0,255)) };
LayoutRoot.Children.Add(cc);
Canvas.SetLeft(cc, 300);
Canvas.SetTop(cc, 300);
Related
I would like to get the width and height of both the borders from code and change them programmatically. I have a button that should change the size of both of them but I don't know how to get them from my class. How can I do it? The size of both is always the same
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto" x:Name="games_list" MouseDoubleClick="Games_list_MouseDoubleClick" Background="{StaticResource DefaultBackgroundColor}" Padding="10" BorderBrush="{x:Null}" Foreground="{x:Null}" SelectionChanged="Games_list_SelectionChanged" KeyDown="Games_list_KeyDown" MouseEnter="Games_list_MouseEnter" MouseLeave="Games_list_MouseLeave" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Margin="10" Width="150" Height="225" Background="{StaticResource DetailsColor}">
<StackPanel>
<Border Width="150" Height="225">
<DockPanel>
<Image Source="/icons/delete.png" Width="24" Height="24" HorizontalAlignment="Left" Margin="2" VerticalAlignment="Top" MouseLeftButtonUp="Delete_MouseLeftButtonUp" >
<Image.Effect>
<DropShadowEffect/>
</Image.Effect>
</Image>
<Image Source="/icons/info.png" Width="24" Height="24" HorizontalAlignment="Right" Margin="2" VerticalAlignment="Top" MouseLeftButtonUp="Info_MouseLeftButtonUp" >
<Image.Effect>
<DropShadowEffect/>
</Image.Effect>
</Image>
</DockPanel>
<Border.Background>
<ImageBrush ImageSource="{Binding Image_path}"/>
</Border.Background>
</Border>
<Rectangle Fill="#4C000000" Margin="0,-20,0,10" Height="20"/>
<TextBlock Text="{Binding Name}" Foreground="White" FontSize="16" TextAlignment="Center" Margin="0,-32,0,44" />
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ListBox>
You only need to set a Binding, the following is a minimalist demo of mine:
(Use the slider to control the width and height of the border.)
<StackPanel Margin="30,40,37,81">
<Border Width="{Binding Path=Value,ElementName=slider1}" Height="{Binding Path=Value,ElementName=slider1}" BorderBrush="Black" Margin="5">
<DockPanel Background="Black">
<Label >TEST</Label>
</DockPanel>
</Border>
<Slider x:Name="slider1" Maximum="100" Minimum="0" Margin="5" />
</StackPanel>
Updated:
xaml:
<Grid Loaded="Grid_Loaded">
<StackPanel Margin="30,40,37,81">
<Border Name="border1" BorderBrush="Black" Margin="5" Loaded="Border_Loaded">
<DockPanel Background="Black">
</DockPanel>
</Border>
</StackPanel>
</Grid>
code:
private void Border_Loaded(object sender, RoutedEventArgs e)
{
int c = 500;
int d = 300;
this.border1.SetBinding(Border.HeightProperty, new Binding(".") { Source = c });
this.border1.SetBinding(Border.WidthProperty, new Binding(".") { Source = d });
}
I have a listbox in WPF which consists of few labels and a rectangle.
I am trying to get all the label values of items in a ListBox.
My WPF markup for ListBox is:
<ListBox x:Name="izabraniList" ItemTemplate="{DynamicResource izabraniIzbornik}" Margin="0,80,10,108" HorizontalAlignment="Right" Width="289" Background="{x:Null}" ScrollViewer.HorizontalScrollBarVisibility="Hidden">
<ListBox.Resources>
<DataTemplate x:Key="izabraniIzbornik">
<Border BorderBrush="white" CornerRadius="2,2,2,2" BorderThickness="1,1,1,1">
<StackPanel Orientation="Horizontal" Width="254" Height="64" UseLayoutRounding="False" Opacity="100">
<DockPanel>
<Rectangle Height="62" Width="62"
Margin="2,0" RadiusX="5" RadiusY="5" >
<Rectangle.Fill>
<ImageBrush ImageSource="{Binding Path=ART_SIFRA, Converter={StaticResource ImageSourceConverter}}"/>
</Rectangle.Fill>
</Rectangle>
</DockPanel>
<DockPanel Width="133" >
<Label Content="{Binding ART_NAZIV}"
VerticalAlignment="Center"
HorizontalAlignment="left"
FontSize="12" Width="auto" Foreground="#FF303030" FontWeight="Bold" />
</DockPanel>
<DockPanel HorizontalAlignment="right" Width="55" Height="64">
<DockPanel HorizontalAlignment="Right" VerticalAlignment="top" Height="20" FlowDirection="RightToLeft"/>
<DockPanel HorizontalAlignment="Right" VerticalAlignment="bottom" Height="64" FlowDirection="RightToLeft" Width="55">
<Label x:Name="cijena" Content="{Binding SKC_PRICE}" FontSize="11" DockPanel.Dock="Right" HorizontalAlignment="Center" VerticalAlignment="Center" FontWeight="Bold" Width="55" />
<Label Content="{Binding kolicina}" FontSize="11" DockPanel.Dock="Right" HorizontalAlignment="left" VerticalAlignment="top" FontWeight="Bold" Width="55" />
</DockPanel>
</DockPanel>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.Resources>
</ListBox>
And I would like to do something like this:
For Each i As String In izabraniList.Items("SKC_PRICE")
Console.WriteLine(i)
Next
make a separate property in your class that flattens the list (i.e.
public string property { get { return String.Join(", ", izabranilist.Select(x => x.Skc_Price).toarray()); } }
and then bind that to the label.
I'm using the following DataTemplate
<DataTemplate x:Key="platform_resources">
<StackPanel Orientation="Horizontal">
<Viewbox Width="30" Height="30" ToolTip="Network Domain Count" Stretch="Uniform">
<ContentControl DataContext="{Binding}" Focusable="False" Content="{DynamicResource appbar_server}" />
</Viewbox>
<TextBlock Margin="0,7,0,0" Text="{Binding Path=workload_count}"/>
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" />
<Viewbox Width="30" Height="30" ToolTip="Logical Network Count" Stretch="Uniform">
<ContentControl Focusable="False" Content="{DynamicResource appbar_network_server_connecting}" />
</Viewbox>
<TextBlock Margin="0,7,0,0" Text="{Binding Path=vlan_count}"/>
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" />
<Viewbox Width="30" Height="30" ToolTip="Network Domain Count" Stretch="Uniform">
<ContentControl Focusable="False" Content="{DynamicResource appbar_network}" />
</Viewbox>
<TextBlock Margin="0,7,0,0" Text="{Binding Path=networkdomain_count}"/>
</StackPanel>
</DataTemplate>
The template displays all the relevant data with separators but only shows the images on the last record. It's leaves spaces where the images are supposed to be, but no images.
Make sure you add x:Shared="False" property to your resources.
Example:
<Canvas x:Key="appbar_server" x:Shared="False">
<!-- ... -->
</Canvas>
This happens because you probably defined some Images (e.g appbar_server) in your resources and trying to display them in multiple items. But Image is a Visual and in WPF each Visual can only have one parent. So when your items are being generated, each item steals the Image from the previous one until the last item finally gets it.
Solution:
Unlike Image, BitmapImage is not a Visual and thus can be set multiple times as the source of different items. So instead of defining Images in your Resources, define BitmapImages:
<Window.Resources>
<BitmapImage x:Key="appbar_server" UriSource="C:\...\appbar_server.png"/>
....
And then instead of ContentControls create Image instances in your DataTemplate to present them:
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Viewbox Width="30" Height="30" ToolTip="Network Domain Count" Stretch="Uniform">
<Image Focusable="False" Source="{DynamicResource appbar_server}" />
</Viewbox>
<TextBlock Margin="0,7,0,0" Text="{Binding Path=workload_count}"/>
...
*Update:
The image is captured in a canvas which seems to be needing some
special wrapper to make this work.
In that case, you should define a DataTemplate for each Canvas like this:
<Window.Resources>
<DataTemplate x:Key="appbar_3d_3ds">
<Canvas Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">
<Path Width="32" Height="40" Canvas.Left="23" Canvas.Top="18" Stretch="Fill" Fill="Black" Data="F1 M 27,18L 23,26L 33,30L 24,38L 33,46L 23,50L 27,58L 45,58L 55,38L 45,18L 27,18 Z "/>
</Canvas>
</DataTemplate>
....
And then create ContentPresenter Instances in your ItemTemplate with their ContentTemplate set to your pre-defined data templates (e.g. appbar_3d_3ds).
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Viewbox Width="30" Height="30" ToolTip="Network Domain Count" Stretch="Uniform">
<ContentPresenter ContentTemplate="{DynamicResource appbar_3d_3ds}"/>
</Viewbox>
<TextBlock Margin="0,7,0,0" Text="{Binding Path=workload_count}"/>
....
Here's my code for the button:
<Button //namespaces
x:Class="myProject.MyButton"
FocusVisualStyle="{x:Null}"
Foreground="White"
Height="60"
Width="200"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Padding="0 0 1 1"
>
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="border" CornerRadius="8">
<Border.Background>
#006BB7
</Border.Background>
<DockPanel Margin="3">
<Image Source="{Binding Path=Tag, RelativeSource={RelativeSource TemplatedParent}}" Height="30" Stretch="Fill" Margin="0 0 0 0" HorizontalAlignment="Left"/>
<TextBlock Text="{Binding Path=Content,RelativeSource={RelativeSource TemplatedParent}}" FontSize="20" VerticalAlignment="Center" Padding="10,0,10,0" HorizontalAlignment="Left"/>
<Image Source="{DynamicResource ResourceKey=Bmp}" Height="30" Width="30" HorizontalAlignment="Right" Margin="0 0 10 0"></Image>
</DockPanel>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
When I call it like this:
<ui:MyButton x:Name="btnGo" Tag="/Resources/Go.png" Content="Go" Command="{Binding Path=GoCommand}" Margin="20 0 0 0"/>
and everything looks like I want it to look: the first image isn't shown, and the third part of the button (the second image) is aligned right.
However, here:
<ui:MyButton Margin="10" Content="{Binding MyName}" Tag="{Binding Path}" HorizontalAlignment="Center" VerticalAlignment="Top">
<Button.Resources>
<BitmapImage x:Key="Bmp" UriSource="/Images/arrowRight.png"></BitmapImage>
</Button.Resources>
</ui:MyButton>
Whn I call the button like this, the text and the second image aren't shown. All that is shown is the first image and it is stretched via the whole button's width. Why is this happening? I want to make this to behave as the other one.
Try this
In Change content of Image and of TextBlock when the Image and the TextBlock are in a button we are using resource for button and here we are using for <ui:MyButton>
<ui:MyButton Margin="10" Content="{Binding MyName}" Tag="{Binding Path}" HorizontalAlignment="Center" VerticalAlignment="Top">
<ui:MyButton.Resources>
<BitmapImage x:Key="Bmp" UriSource="/Images/arrowRight.png"></BitmapImage>
</ui:MyButton.Resources>
</ui:MyButton>
I have a particular template for a listbox item which contains different textblocks. After adding the items in the listbox I want to retrieve the text of a particular textblock of a particular listbox item. How can I do that?
<ListBox x:Name="listBox1" Grid.Row="0" SelectionChanged="listBox1_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0,2,0,0">
<CheckBox Name="cbSelect" VerticalAlignment="Center" Visibility="{Binding isVisible}"/>
<StackPanel Orientation="Vertical">
<Grid HorizontalAlignment="Stretch" >
<TextBlock Name="txtTitle" VerticalAlignment="Bottom" Text="{Binding Title}" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
<TextBlock Name="txtDate" Foreground="{StaticResource PhoneInverseInactiveBrush}" HorizontalAlignment="Right" VerticalAlignment="Center" Text="{Binding CreatedOn}" Margin="0,0,30,0" Style="{StaticResource PhoneTextSmallStyle}" />
<!-- <Image x:Name="lineImg" Source="/Icons/appbar.next.rest.png" HorizontalAlignment="Right" VerticalAlignment="Bottom" Width="48" Height="48"/>-->
</Grid>
<TextBlock Name="txtContent" Foreground="{StaticResource PhoneDisabledBrush}" VerticalAlignment="Bottom" Text="{Binding Content}" Style="{StaticResource PhoneTextNormalStyle}" />
<Line Stroke="{StaticResource PhoneBorderBrush}" StrokeThickness=" 2" X1="0" Y1="0" X2="{Binding ElementName=listBox1, Path=ActualWidth}" Y2="0" Margin="0,6,0,0" ></Line>
<!-- <Image x:Name="lineImg" Source="/Icons/Line.png" HorizontalAlignment="Center" Width="500" Height="2" Margin="0,15,0,0" />-->
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In the above code, after adding the items to the listbox, I want the text of txtTitle of a particular item. How can I get it?
You need to get a reference to the ContentPresenter of the item.
ListBoxItem myListBoxItem = (ListBoxItem)(myListBox.ItemContainerGenerator.ContainerFromItem(myListBox.Items.CurrentItem));
ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(myListBoxItem);
DataTemplate myDataTemplate = myContentPresenter.ContentTemplate;
TextBlock myTextBlock = (TextBlock)myDataTemplate.FindName("textBlock", myContentPresenter);
The FindVisualChild method along with the full example can be found here.