Arranging issue when adding dynamic elements - c#

I am adding dynamic elements to the screen. When the elements fill up the container I am putting them in, I want a vertical scroll bar to appear so that the user can scroll through all of the elements added. The code is performing as expected.
The problem is that when the elements are added to the interface, they are added in a haphazard and weird way. They start in the middle of the grid element, and then shift up. I want them to be added to the top left of the control, then to the right, all the way down the page with equal spacing between the rows. Then when there are too many on the screen, have the scroll bar appear.
Here is all of the code that is running this test. Thanks in advance for any help!
xaml:
<Window x:Class="GPWorkouts.MainWindow"
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"
xmlns:local="clr-namespace:GPWorkouts"
mc:Ignorable="d"
Title="MainWindow" Height="370" Width="1000" >
<Grid>
<StackPanel Orientation="Horizontal">
<StackPanel Width="500">
<StackPanel Orientation="Horizontal">
<Button Name="buttonAddNewPlayer" Content="Add New Player" Margin="5" Click="buttonAddNewPlayer_Click" />
</StackPanel>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Hidden" Height="300">
<UniformGrid Columns="2" Name="playerContainer" ScrollViewer.HorizontalScrollBarVisibility="Visible" ScrollViewer.CanContentScroll="True" >
</UniformGrid>
</ScrollViewer>
</StackPanel>
</StackPanel>
</Grid>
</Window>
c# code behind:
using log4net;
using System.Windows;
using System.Windows.Controls;
namespace GPWorkouts
{
public partial class MainWindow : Window
{
private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
int x = 1;
public MainWindow()
{
InitializeComponent();
}
private void buttonAddNewPlayer_Click(object sender, RoutedEventArgs e)
{
Button buttonDyno = new Button()
{
Content = x,
Width = 150,
Height = 32,
Margin = new Thickness(5),
};
x++;
playerContainer.Children.Add(buttonDyno);
}
}
}

the observed effect can be explaned by UniformGrid arrange approach. Buttons start in the middle of the grid element, and then shift up, because UniformGrid has less and less space after each button added.
the effect can be smoothed if you add VerticalAlignment = VerticalAlignment.Top in Button properties.
but I would suggest to use a different panel : WrapPanel
<Grid>
<StackPanel Orientation="Horizontal">
<StackPanel Width="500">
<StackPanel Orientation="Horizontal">
<Button Name="buttonAddNewPlayer"
Margin="5"
Click="buttonAddNewPlayer_Click"
Content="Add New Player" />
</StackPanel>
<ScrollViewer Height="300"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<WrapPanel Name="playerContainer"
Background="Wheat"
Orientation="Horizontal" />
</ScrollViewer>
</StackPanel>
</StackPanel>
</Grid>
private void buttonAddNewPlayer_Click(object sender, RoutedEventArgs e)
{
Button buttonDyno = new Button
{
Content = x,
Width = 150,
Height = 32,
Margin = new Thickness(5),
VerticalAlignment = VerticalAlignment.Top,
HorizontalAlignment = HorizontalAlignment.Center
};
x++;
playerContainer.Children.Add(buttonDyno);
// ensure 2 controls per row
playerContainer.ItemWidth = playerContainer.ActualWidth/2;
}

Related

Show controls in Scrollviewer row along the Scrollbar

So I have this scenario in which I am showing a Grid inside a ScrollViewer.
I want to show a combobox and an image along the scrollbar in a way that it doesn't effect the scrolling functionality,
Something like this:
Currently whenever the scrollviewer becomes visible it appears in a new row, how can I show it along the controls in the same row?
Here is my xaml design:
<DockPanel LastChildFill="True">
<!--Top Panel-->
<Grid DockPanel.Dock="Top">
--GridContent
</Grid>
<!--Bottom Panel-->
<Grid DockPanel.Dock="Bottom">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ComboBox Grid.Column ="0">
</ComboBox>
<Image Grid.Column="1">
</Image>
</Grid>
<ScrollViewer HorizontalScrollBarVisibility="Auto" HorizontalAlignment="Stretch"
VerticalScrollBarVisibility="Auto" VerticalAlignment="Stretch" >
<Grid
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
-- Grid Content
</Grid>
</ScrollViewer>
</DockPanel>
Currently it appears like this :
I haven't looked at doing this in XAML, but you can do it like this in the code behind:
public partial class MainWindow : Window
{
private void IncrementColumn(UIElement element)
{
Grid.SetColumn(element, Grid.GetColumn(element) + 1);
}
public MainWindow()
{
InitializeComponent();
scrollPanel.ApplyTemplate();
var horizontal = scrollPanel.Template.FindName("PART_HorizontalScrollBar", scrollPanel) as ScrollBar;
var vertical = scrollPanel.Template.FindName("PART_VerticalScrollBar", scrollPanel) as ScrollBar;
var presenter = scrollPanel.Template.FindName("PART_ScrollContentPresenter", scrollPanel) as ScrollContentPresenter;
var corner = scrollPanel.Template.FindName("Corner", scrollPanel) as Rectangle;
var grid = corner.Parent as Grid;
grid.ColumnDefinitions.Insert(0, new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Auto) });
IncrementColumn(horizontal);
IncrementColumn(vertical);
IncrementColumn(corner);
Grid.SetColumnSpan(presenter, 2);
var panel = new StackPanel() { Orientation = Orientation.Horizontal };
panel.Children.Add(new ComboBox());
panel.Children.Add(new Image());
Grid.SetRow(panel, 1);
Grid.SetColumn(panel, 0);
grid.Children.Add(panel);
}
}
Here's the XAML to go with it:
<Window x:Class="WpfApplication1.MainWindow"
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"
Title="MainWindow" Height="150" Width="525">
<ScrollViewer
Name="scrollPanel"
HorizontalScrollBarVisibility="Visible"
HorizontalAlignment="Stretch"
VerticalScrollBarVisibility="Auto"
VerticalAlignment="Stretch">
<Grid
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Image Source="C:\Users\Public\Pictures\Sample Pictures\Desert.jpg"/>
</Grid>
</ScrollViewer>
</Window>

Programmatically adding items to usercontrol

My MainWindow.xaml get it´s datacontext from MyClass.cs and it´s view from usercontrol in MyView.xaml as below
<Window.DataContext>
<ViewModel:MyClass/>
</Window.DataContext>
<View:MyView/>
The usercontrol has grid, stackpanel and a button as below
<Grid x:Name="MainGrid" Height="300" Width="502">
<StackPanel x:Name="MainStackPanel" Height="200" Width="400"/>
<Button x:Name="NewButton" Command="{Binding CommandAddNewButton}" Content="new item" Height="25" Width="55" Margin="224,177,223,98"/>
</Grid>
From MyClass I try to add button and textbox to the stackpanel programatically by pressing the "new item" button as below
public ICommand CommandAddNewButton
{ get { return new MyCommand(AddNewButton); } }
private void AddNewButton()
{
var newButton = new Button
{
Name = "Button" + _itemNbr,
Content = "-",
FontSize = 10,
Height = 20,
Width = 15,
Background = new SolidColorBrush(Colors.Beige),
HorizontalContentAlignment = HorizontalAlignment.Center,
VerticalContentAlignment = VerticalAlignment.Center,
Margin = new Thickness(10, 10, 10, 10)
};
var newTextBox = new TextBox
{
Name = "TextBox" + _itemNbr,
Height = 20,
Width = 70,
FontSize = 10,
HorizontalContentAlignment = HorizontalAlignment.Right,
VerticalContentAlignment = VerticalAlignment.Center
};
MainStackPanel.Children.Add(newButton);
MainStackPanel.Children.Add(newTextBox);
}
A new button and textboxe are created after button press but they do not displayed. How can I fix it? All help are appreciated.
<UserControl x:Class="WpfApp1.View.MyView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup- compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ViewModel="clr-namespace:WpfApp1.ViewModel"
xmlns:View="clr-namespace:WpfApp1.View">
<Grid x:Name="MainGrid" Height="300" Width="502">
<StackPanel x:Name="MainStackPanel" Height="200" Width="400"/>
<Button x:Name="NewButton" Command="{Binding CommandAddNewButton}" Content="new item" Height="25" Width="55" Margin="224,177,223,98"/>
</Grid>
</UserControl>
I think you are inheriting your View(Myview) to your viewmodel(Myclass). so that you are accessing a different instance of stackpanel. One method you can do is pass stackpanel as a paramater to the view model .
In Xaml:
<Button x:Name="NewButton" Command="{Binding CommandAddNewButton}" CommandParameter="{Binding ElementName=MainStackPanel}" Content="new item" Height="25" Width="55" Margin="224,177,223,98"/>
In ViewModel:
private void AddNewButton(object obj)
{
var MainStackPanel= obj as StackPanel;
}

ScrollViewer ChangeView on SizeChanged

I have DataTemplate which I use in HubSection:
<DataTemplate x:Name="dataTemplate2">
<Grid x:Name="greedy">
<ScrollViewer x:Name="scroller" SizeChanged="ScrollViewer_SizeChanged" Height="{Binding Height,ElementName=greedy}" >
<ItemsControl x:Name="itemsControl"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource dataTemplateDetails}">
<ItemsControl.ItemContainerTransitions>
<TransitionCollection>
<ReorderThemeTransition />
<NavigationThemeTransition />
</TransitionCollection>
</ItemsControl.ItemContainerTransitions>
</ItemsControl>
</ScrollViewer>
</Grid>
</DataTemplate>
In my ItemsControl I have items which can be expandable. What I want to achieve is that when the item will expand to see more details about that item. I want Scrollviewer to scroll down (for amount of changed size of ScrollViewer).
Code behind for SizeChanged event:
private void ScrollViewer_SizeChanged(object sender, SizeChangedEventArgs e)
{
ScrollViewer myScroll = (ScrollViewer)sender;
myScroll.ChangeView(null, myScroll.ScrollableHeight, null, false);
}
What I have done now is not working as my expectation. I scroll down to the end right now. But the thing is it scrolls only when the items extend the size of available view (ScrollBar is shown). Then if I expand another item it doesn't work. If I hide details about item (ScrollBar hides too) and expand it again it will work again.
It's like the SizeChanged event occures only when ScrollViewer is going into action but have infinite height which doesn't change.
I've tried Grid with row set to "*", it changes nothing. Now I try to set height by binding it to height of ItemsControl - still the same behaviour.
Could you help me with the solution, show the path of thinking or enlighten me with some workaround?
EDIT:
I prepared some code to work with to see what happens exactly.
1) Create New Project -> Store Apps (c#) -> Windows Phone 8.1 (Blank App) and name it "scroll"
2) Paste this code into MainPage.xaml
<Page
x:Class="scroll.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:scroll"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="DarkOliveGreen">
<Page.Resources>
<DataTemplate x:Name="dataTemplateDetails">
<Grid Name="grido" Grid.Row="1" Margin="10,10,10,10">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="Auto" />
<RowDefinition Height="50" />
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<Border Grid.Row="0" Background="Black" CornerRadius="10" Opacity="0.4" />
<Border Grid.Row="1" Background="Black" CornerRadius="10" Opacity="0.3" />
<Border Grid.Row="2" Background="Black" CornerRadius="10" Opacity="0.2" />
<Border Grid.Row="3" Background="Black" CornerRadius="10" Opacity="0.1" />
<TextBlock Grid.Row="0" Text="{Binding Name}" Style="{StaticResource BaseTextBlockStyle}" HorizontalAlignment="Center"/>
<Grid Grid.Row="1" HorizontalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Margin="5,0,5,0" Text="Description:" Style="{StaticResource BaseTextBlockStyle}"/>
<TextBlock Grid.Column="1" Text="{Binding Description}" Style="{StaticResource BaseTextBlockStyle}"/>
</Grid>
<TextBlock Grid.Row="2" Text="Next row" Style="{StaticResource BaseTextBlockStyle}" HorizontalAlignment="Center"/>
<TextBlock Grid.Row="3" Text="Next row" Style="{StaticResource BaseTextBlockStyle}" HorizontalAlignment="Center"/>
</Grid>
</DataTemplate>
<DataTemplate x:Name="dataTemplate2">
<Grid x:Name="greedy" >
<ScrollViewer x:Name="scroller" SizeChanged="ScrollViewer_SizeChanged" VerticalAlignment="Top">
<ItemsControl x:Name="itemsControl"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource dataTemplateDetails}">
<ItemsControl.ItemContainerTransitions>
<TransitionCollection>
<ReorderThemeTransition />
<NavigationThemeTransition />
</TransitionCollection>
</ItemsControl.ItemContainerTransitions>
</ItemsControl>
</ScrollViewer>
</Grid>
</DataTemplate>
</Page.Resources>
<Page.BottomAppBar>
<CommandBar Background="Black" Opacity="0.6" x:Name="myCommandBar">
<AppBarButton Icon="Add" Label="Add" x:Name="AddItem" Click="Add_Click"/>
<AppBarButton Icon="Delete" Label="Delete" x:Name="RemoveItem" Click="Delete_Click"/>
</CommandBar>
</Page.BottomAppBar>
<Grid>
<Hub x:Name="myHub" Header="Test">
<HubSection x:Uid="myDetailsHubsection" x:Name="myDetailsHubsection" Header="Details" DataContext="{Binding Items}" ContentTemplate="{StaticResource dataTemplate2}" />
</Hub>
</Grid>
3) Paste this code into MainPage.xaml.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
namespace scroll
{
public sealed partial class MainPage : Page
{
public static Details dataContextItems;
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
dataContextItems = new Details();
}
public class TestItem
{
public string Name { get; set; }
public string Description { get; set; }
public TestItem(string n, string d)
{
Name = n;
Description = d;
}
}
public class Details : INotifyPropertyChanged
{
private ObservableCollection<TestItem> _items;
public ObservableCollection<TestItem> Items
{
set
{
_items = value;
NotifyPropertyChanged("Items");
}
get
{
return _items;
}
}
public Details()
{
_items = new ObservableCollection<TestItem>();
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
this.DataContext = dataContextItems;
}
private void Add_Click(object sender, RoutedEventArgs e)
{
TestItem iAmAnItem = new TestItem("Name of an item", "Long and detailed description of an item");
dataContextItems.Items.Add(iAmAnItem);
}
private void Delete_Click(object sender, RoutedEventArgs e)
{
if (dataContextItems.Items.Count > 0)
dataContextItems.Items.RemoveAt(0);
}
private void ScrollViewer_SizeChanged(object sender, SizeChangedEventArgs e)
{
ScrollViewer myScroll = (ScrollViewer)sender;
myScroll.ChangeView(null, myScroll.ScrollableHeight, null, false);
}
}
}
4) Run the app
5) When you add first two items you can't scroll them but when you add more items you can see that it scrolls down as soon as items "need more space" to be shown and scrollbar occures. But with adding more items it doesn't work. If you delete items and add "third" item again it will scroll down.
I want it to scroll down everytime size of scrollviewer changes (in this case when new item occures but keep in mind it should work when item "extends" in my original solution and there can be few extended items simultaneously).
I've been tinkering with the solution and I finally found a way to do that.
I think the problem is that I didn't understand how ScrollViewer does work. I took the scrolling height as UIElement height hoping for SizeChanged to be fired what isn't truth. ScrollViewer wasn't changing its size because it just took the whole space it could and then just displayed how much content it is in it (It's like ScrollViewer has almost always infinite height unless it's less than actual available view space). With adding first two items SizeChanged event was firing with third one too and then nothing happend. It proves that.
I needed SizeChanged to be fired everytime the size of ScrollViewer (or in this case the Grid) was changing. Solution is very simple but still it needs understanding of how ScrollViewer works - and now it seems so obvious that it will never be greater than available space.
Changes made to make it work:
<DataTemplate x:Name="dataTemplate2">
<ScrollViewer x:Name="scroller" VerticalAlignment="Top" HorizontalAlignment="Stretch" IsEnabled="True" >
<Grid VerticalAlignment="Top" x:Name="greedo" SizeChanged="greedo_SizeChanged">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ItemsControl x:Name="itemsControl"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource dataTemplateDetails}">
<ItemsControl.ItemContainerTransitions>
<TransitionCollection>
<ReorderThemeTransition />
<NavigationThemeTransition />
</TransitionCollection>
</ItemsControl.ItemContainerTransitions>
</ItemsControl>
</Grid>
</ScrollViewer>
</DataTemplate>
and code behind:
private void greedo_SizeChanged(object sender, SizeChangedEventArgs e)
{
Grid takingScroll = (Grid)sender;
ScrollViewer myScroll = (ScrollViewer)takingScroll.Parent;
myScroll.ChangeView(null, myScroll.ScrollableHeight, null, false);
}

NullPointerException after going back in a NavigationWindow

thats my navigation window
<NavigationWindow x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="800" Width="600" Source="Page1.xaml">
thats my page1
<Page x:Class="WpfApplication1.Page1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="600" d:DesignWidth="800"
Title="Page1" Name="IndexPage">
<ListView Name="myListView" ItemsSource="{Binding ElementName=IndexPage, Path=SeriesCollection}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" IsSynchronizedWithCurrentItem="True" SelectionChanged="handleSelected">
<ListView.ItemsPanel >
<ItemsPanelTemplate>
<WrapPanel>
</WrapPanel>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel >
<Image Width="214" Height="317" Source="{Binding Image}"/>
<Label Content="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Page 2 is just en empty skeleton
code behind
namespace WpfApplication1
{
/// <summary>
/// Interaktionslogik für Page1.xaml
/// </summary>
public partial class Page1 : Page
{
private ObservableCollection<Series> _series =
new ObservableCollection<Series>();
public ObservableCollection<Series> SeriesCollection
{
get { return _series; }
}
public Page1()
{
InitializeComponent();
DirectoryInfo baseDir = new DirectoryInfo(#"C:\Serien");
DirectoryInfo[] dirs = baseDir.GetDirectories();
foreach (DirectoryInfo dir in dirs)
{
Series serie = new Series(dir);
Console.WriteLine("adding " + serie.Name);
_series.Add(serie);
}
Console.WriteLine(_series.Count);
}
public void handleSelected(object sender, RoutedEventArgs args)
{
Series currentSerie = (Series) myListView.Items.CurrentItem;
Page2 page = new Page2();
this.NavigationService.Navigate(page);
Console.WriteLine(currentSerie.Name);
Console.WriteLine(currentSerie.GetType());
Console.WriteLine(currentSerie.ToString());
}
}
}
so i click on an item to trigger the SelectionChanged Event to handle it in SelectionChanged where i navigate to page2 , so far so good.
then i use the back button from the navigation window and get stuck with an NullpointerException at
this.NavigationService.Navigate(page);
i dont even know why this method is triggered. So obviosly i am doing something stupid. Pls tell me what it is. Thanks for your time and affort.
The problem here is that you handle the wrong event. I assume that you want to open Page2 by clicking a ListViewItem. Therefore you should use mouse events instead of SelectionChanged.
For example, you can subscribe to StackPanel MouseDown event in your DataTemplate:
<DataTemplate>
<StackPanel Background="Transparent"
MouseDown="StackPanel_MouseDown">
<Image Width="214" Height="317" Source="{Binding Image}"/>
<Label Content="{Binding Name}"/>
</StackPanel>
</DataTemplate>
You can access clicked Series using the following:
private void StackPanel_MouseDown(object sender, MouseButtonEventArgs e)
{
var currentSerie = (Series)((StackPanel)sender).DataContext;
...
}
UPD If you need a real click, you may use a trick like this:
<DataTemplate>
<Button Click="Button_Click">
<Button.Template>
<ControlTemplate TargetType="Button">
<ContentPresenter/>
</ControlTemplate>
</Button.Template>
<StackPanel Background="Transparent">
<Image Width="214" Height="317" Source="{Binding Image}"/>
<Label Content="{Binding Name}"/>
</StackPanel>
</Button>
</DataTemplate>
We use a Button like a view-model which is able to handle clicks.

How to override MeasureOverride to find the size of ItemsControl

I am developing a UserControl that consists of a block with a heading and a list of items (as ItemsControl). The usercontrol is added dynamically to a canvas. I need to get the actual size of the control (including space taken by ItemsControl) before it gets rendered. I tried overriding MeasureOverride method of the UserControl hoping that the size would be reflected in DesiredSize property. But it is not working.
The XAML is:
<UserControl x:Class="MyTools.MyControl"
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"
mc:Ignorable="d"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid x:Name="LayoutRoot" Background="White">
<Border Name="MainBorder" CornerRadius="5" BorderThickness="2" BorderBrush="Black">
<Grid Name="grid1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" >
<Grid.RowDefinitions>
<RowDefinition Height="34" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Name="titleGrid" Grid.Row="0" Background="#FF727272">
<TextBlock Name="titleText" HorizontalAlignment="Center" Text="{Binding ControlName}" VerticalAlignment="Center" FontSize="13" FontWeight="Bold" Foreground="Beige" />
</Grid>
<Grid Name="gridpr" Grid.Row="1" Background="#12C48F35">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border Name="borderPr" CornerRadius="3" Margin="10" BorderThickness="1" BorderBrush="LightGray" Grid.Row="0">
<Grid Name="gridPr" Background="#FFC1C1C1" MouseLeftButtonUp="gridPr_MouseLeftButtonUp">
<StackPanel>
<TextBlock HorizontalAlignment="Center" Name="txtPr" Text="SubItems" VerticalAlignment="Center" Foreground="#FF584848" FontSize="12" />
<ItemsControl x:Name="pitems" ItemsSource="{Binding MyItems}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="15,0,0,0">
<TextBlock Text="{Binding MyVal}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
</Border>
</Grid>
</Grid>
</Border>
</Grid>
</UserControl>
I am overriding MeasureOverride of the UserControl as shown below:
namespace MyTools
{
public partial class MyControl : UserControl
{
public MyControl()
{
InitializeComponent();
}
public string ControlName { get; set; }
public object MyItems { get; set; }
public class Row
{
public string MyVal { get; set; }
}
protected override Size MeasureOverride(Size availableSize)
{
var desiredSize = base.MeasureOverride(availableSize);
var sideLength = Math.Min(desiredSize.Width, desiredSize.Height);
desiredSize.Width = sideLength;
desiredSize.Height = sideLength;
return desiredSize;
}
}
}
Client Code:
MyControl control1 = new MyControl();
control1.ControlName = "Test Name";
var test = new List<MyControl.Row>(
new MyControl.Row[]
{
new MyControl.Row {MyVal = "Item1"},
new MyControl.Row {MyVal = "Item2"},
new MyControl.Row {MyVal = "Item3"}
});
control1.MyItems = test;
control1.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
MessageBox.Show(control1.DesiredSize.Height.ToString());
canvas1.Children.Add(control1);
I am not getting the actual size using the DesiredSize.Height property from the client. Any idea on how to fix this?
Is your user control defined to change in height to reflect the size of the contents? By default your user control will be a fixed size and not change in height just because the items control has more entries.
I think you need to add your user control inside a grid before then measuring the grid. This is how I measure controls and it seems to work well, even when measuring the control directly does not work as in your case...
MyControl control1 = new MyControl();
... your setup code for control1...
Dim containerGrid As New Grid
containerGrid.Children.Add(control1)
containerGrid.Measure(New Size(Double.MaxValue, Double.MaxValue))
containerGrid.Arrange(New Rect(0, 0, Double.MaxValue, Double.MaxValue))
...now check the grid.ActualWidth and grid.ActualHeight
Try using ActualWidth and ActualHeight properties.
protected override Size MeasureOverride(Size availableSize)
{
var desiredSize = new Size();
var sideLength = Math.Min(ActualWidth, ActualHeight);
desiredSize.Width = sideLength;
desiredSize.Height = sideLength;
return desiredSize;
}

Categories

Resources