I am trying to create a UWP puzzle game, I want to cut the picture into n parts and then show the pieces in a grid.
My problem is, how to force a certain NxN style. Right now I have to maximize the window in order to see a 3x3 grid, if I shrink either side, it will converge to a 2 column, 1 column grid. Is there a way to handle this?
This is what I have done, I know the RowDefinition is manually right now, until I figure out a better way to do that.
<UserControl
x:Class="PictureSplitter.Views.PictureView"
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"
d:DesignHeight="300"
d:DesignWidth="400">
<GridView ItemsSource="{Binding Splitter.PuzzlePositions}">
<GridView.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Red" BorderThickness="2">
<Grid x:Name="picGrid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Image Source="{Binding Piece.ImageSource}" />
</Grid>
</Border>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</UserControl>
This are two example images:
There are probably couple of ways to do that, here is another one. I've modified the UserControl so that it automatically adjusts items size to show them as square grid, when page size changes and/or collection changes.
The UserControl XAML code:
<UserControl
x:Class="MyControls.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyControls"
Name="myControl">
<GridView Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" ItemsSource="{Binding ElementName=myControl, Path=Items}"
Width="{Binding ElementName=myControl, Path=CurrentWidth}" HorizontalAlignment="Center"
Height="{Binding Width, RelativeSource={RelativeSource Self}}">
<GridView.ItemContainerStyle>
<Style TargetType="GridViewItem">
<Setter Property="Margin" Value="0"/>
</Style>
</GridView.ItemContainerStyle>
<GridView.ItemTemplate>
<DataTemplate>
<Border Padding="10" Width="{Binding ElementName=myControl, Path=ElementSize}" Height="{Binding ElementName=Width, RelativeSource={RelativeSource Self}}">
<Border BorderBrush="Red" BorderThickness="3">
<Image Source="ms-appx:///Assets/StoreLogo.png" Stretch="UniformToFill"/>
</Border>
</Border>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</UserControl>
UserControl code behind:
public sealed partial class MyUserControl : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void RaiseProperty(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
public IList Items
{
get { return (IList)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register("Items", typeof(IList), typeof(MyUserControl),
new PropertyMetadata(0, (s, e) =>
{
if (Math.Sqrt((e.NewValue as IList).Count) % 1 != 0)
Debug.WriteLine("Bad Collection");
}));
public void Items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (Math.Sqrt(Items.Count) % 1 != 0) Debug.WriteLine("Bad Collection");
RaiseProperty(nameof(ElementSize));
}
private double currentWidth;
public double CurrentWidth
{
get { return currentWidth; }
set { currentWidth = value; RaiseProperty(nameof(CurrentWidth)); RaiseProperty(nameof(ElementSize)); }
}
public double ElementSize => (int)(currentWidth / (int)Math.Sqrt(Items.Count)) - 1;
public MyUserControl()
{
this.InitializeComponent();
}
}
The MainPage XAML:
<Grid>
<local:MyUserControl x:Name="myControl" Items="{Binding MyItems}"/>
<Button Content="Add" Click="Button_Click"/>
</Grid>
MainPage code behind:
public sealed partial class MainPage : Page
{
private ObservableCollection<int> myItems = new ObservableCollection<int> { 1, 2, 3, 4, 5, 6, 7, 8 };
public ObservableCollection<int> MyItems
{
get { return myItems; }
set { myItems = value; }
}
public MainPage()
{
this.InitializeComponent();
DataContext = this;
MyItems.CollectionChanged += myControl.Items_CollectionChanged;
}
protected override Size MeasureOverride(Size availableSize)
{
myControl.CurrentWidth = Math.Min(availableSize.Height, availableSize.Width);
return base.MeasureOverride(availableSize);
}
private void Button_Click(object sender, RoutedEventArgs e) => MyItems.Add(3);
}
The program starts with "Bad Collection" - there are 8 items, so you can't make a square grid from them, but as soon as you click the provided button - the collection's count changes to 9 and the grid should update itself.
It looks like you are doing this by way of MVVM, so I think you need to have a property for your Rows and Columns from your ViewModel. And then you need to have a Converter to supply the coordinate for your pieces .... OR an Attached property.
This will give you an idea:
<Window.Resources>
<System:Int64 x:Key="X">3</System:Int64>
<System:Int64 x:Key="Y">3</System:Int64>
</Window.Resources>
<Grid x:Name="myGrid" Loaded="Grid_Loaded">
// You can bind column and row
// <Button Content="image1" Grid.Column="{Binding column}" Grid.Row="{Binding row}"/>
<Button Content="image1" Grid.Column="0" Grid.Row="0"/>
<Button Content="image2" Grid.Column="1" Grid.Row="0"/>
<Button Content="image3" Grid.Column="2" Grid.Row="0"/>
<Button Content="image4" Grid.Column="0" Grid.Row="1"/>
<Button Content="image5" Grid.Column="1" Grid.Row="1"/>
<Button Content="image6" Grid.Column="2" Grid.Row="1"/>
<Button Content="image7" Grid.Column="0" Grid.Row="2"/>
<Button Content="image8" Grid.Column="1" Grid.Row="2"/>
<Button Content="image9" Grid.Column="2" Grid.Row="2"/>
</Grid>
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
Int64 X = (Int64) this.FindResource("X");
Int64 Y = (Int64) this.FindResource("Y");
for (Int64 i = 0; i < X; i++)
{
ColumnDefinition c = new ColumnDefinition();
myGrid.ColumnDefinitions.Add(c);
}
for (Int64 i = 0; i < (int)Y; i++)
{
RowDefinition r = new RowDefinition();
myGrid.RowDefinitions.Add(r);
}
}
I have used a ListView with GridView as it's View property. And it is working fine.
<ListView x:Name="ImageList" Width="210" Height="210">
<ListView.View>
<GridView>
<GridView.ColumnHeaderContainerStyle>
<Style TargetType="Control">
<Setter Property="Visibility" Value="Collapsed"/>
</Style>
</GridView.ColumnHeaderContainerStyle>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding sq1}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn >
<GridViewColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding sq2}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn >
<GridViewColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding sq3}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
var imgBox = new BitmapImage(new Uri(#"/images/cellbkg.jpg", UriKind.Relative));
var source = new[] { new { sq1 = imgBox, sq2 = imgBox, sq3 = imgBox }, new { sq1 = imgBox, sq2 = imgBox, sq3 = imgBox }, new { sq1 = imgBox, sq2 = imgBox, sq3 = imgBox } };
ImageList.ItemsSource = source;
This code produces below output, and don't get collapsed if you reduce window size :
If this is what you want, you can add columns dynamically using below approach. For NxN matrix, you have to add only N columns, binding will take care of rest :
GridView view = (GridView)ImageList.View;
view.Columns.Add(new GridViewColumn());
Related
I am trying to dynamically create DataGrids in WPF, one below the other in a Grid. the problem is that I don't even know where to start, I saw that it could be done through code but I would like to use XAML correctly.
void PopulateDatagridSQL(string Data, string index, string TabName)
{
try
{
Data = Data.Replace((Char)6, (Char)124);
List<DataTable> Results = new List<DataTable>();
Results = JsonConvert.DeserializeObject<List<DataTable>>(Data);
foreach (SQLWindow SingleSQLWindows in StaticVar.MySQLWindows)
{
if (SingleSQLWindows.MyINDEX == index)
{
SingleSQLWindows.Dispatcher.Invoke(new Action(() =>
{
foreach (TabItem item in SingleSQLWindows._tabItems)
{
if (item.Name == TabName)
{
//create multiple datagrids up to results.count
((SQLPage)((Frame)item.Content).Content).DataGrid1.ItemsSource = Results[0].DefaultView;//foreach
((SQLPage)((Frame)item.Content).Content).TxtSqlLog.Text = "Records in Datagrid: " + Results[0].Rows.Count;//foreach
}
}
}));
}
}
}
catch (Exception asd)
{
foreach (SQLWindow SingleSQLWindows in StaticVar.MySQLWindows)
{
if (SingleSQLWindows.MyINDEX == index)
{
SingleSQLWindows.Dispatcher.Invoke(new Action(() =>
{
foreach (TabItem item in SingleSQLWindows._tabItems)
{
if (item.Name == TabName)
{
((SQLPage)((Frame)item.Content).Content).TxtSqlLog.Text = "Error in $SqlResponse";
}
}
}));
}
}
}
}
In this function I receive a list of DataTables and for each DataTable I have to create a DataGrid.
<Page x:Class="Emergency_APP_Server_WPF.Forms.SQLPage"
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:local="clr-namespace:Emergency_APP_Server_WPF.Forms"
mc:Ignorable="d" x:Name="SQLPageXaml" Loaded="SQLPage_Loaded"
d:DesignHeight="450" d:DesignWidth="800"
Title="SQLPage" >
<Grid>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" MinHeight="100"></RowDefinition>
<RowDefinition Height="0"></RowDefinition>
<RowDefinition Height="*" MinHeight="150"></RowDefinition>
<RowDefinition Height="35"></RowDefinition>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="70"></ColumnDefinition>
</Grid.ColumnDefinitions>
<avalonEdit:TextEditor Grid.Column="0" Background="White"
xmlns:avalonEdit="http://icsharpcode.net/sharpdevelop/avalonedit"
Name="MyAvalonEdit" FontFamily="Consolas"
FontSize="11pt" Margin="10,10,10,10" ShowLineNumbers="True"
LineNumbersForeground="Gray" ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto" KeyUp="MyAvalonEdit_KeyUp"/>
<Button Grid.Column="1" x:Name="BtnSendSQL" Margin="0,10,10,10" Click="BtnSendSQL_Click">
<StackPanel>
<Image Height="25" Source="..//Resources/send.png"></Image>
<TextBlock Margin="0,10,0,0" VerticalAlignment="Top" Foreground="White" Text="SendSQL"></TextBlock>
</StackPanel>
</Button>
</Grid>
<GridSplitter Margin="0,-10,0,0" Grid.Row="1" HorizontalAlignment="Stretch" Background="Transparent" ResizeBehavior="PreviousAndNext">
</GridSplitter>
<DataGrid Grid.Row="2" x:Name="DataGrid1" RowStyle="{StaticResource DataGridRowSql}" Style="{StaticResource DataGridStyleSQL}" >
<DataGrid.CommandBindings>
<CommandBinding Command="Copy" Executed="CommandBinding_Executed"></CommandBinding>
</DataGrid.CommandBindings>
<DataGrid.InputBindings>
<KeyBinding Key="C" Modifiers="Ctrl" Command="Copy"></KeyBinding>
</DataGrid.InputBindings>
</DataGrid>
<TextBlock TextAlignment="Center" TextWrapping="Wrap" VerticalAlignment="Bottom" Grid.Row="3" x:Name="TxtSqlLog" Text="Wait For Commands..."
FontSize="14" FontFamily="Consolas" Foreground="White"></TextBlock>
</Grid>
</Grid>
I do not know the exact layout where you want to put all the DataGrids, so I just assume you want to place them in row 2 of your Grid. As you want to stack them, you can use an ItemsControl in a ScrollViewer. The latter is just there, so you can scroll through the DataGrids in case the page is too small to display them all. So this is the basic layout for the solutions below.
<Grid>
<Grid.RowDefinitions>
<!-- ...your row definitions. -->
</Grid.RowDefinitions>
<!-- ...your other controls. -->
<ScrollViewer Grid.Row="2">
<ItemsControl/>
</ScrollViewer>
</Grid>
Code-behind solution
In code-behind, you need to access the ItemsControl, so assign it a name.
<ItemsControl x:Name="DataGridContainer"/>
Then just create DataGrids in a loop from your DataTable list.
foreach (var dataTable in Results)
{
var dataGrid = new DataGrid { ItemsSource = dataTable.DefaultView };
DataGridContainer.Items.Add(dataGrid);
};
By the way, in this scenario, you could also use a StackPanel instead of an ItemsControl.
MVVM solution
You can create a property in your view model that exposes the default views of your DataTables.
public ObservableCollection<DataView> DataViews { get; }
Make sure you instantiate the collection in the constructor or implement INotifyPropertyChanged, so that the collection is available in XAML. Then bind the ItemsControl to this collection.
<ItemsControl ItemsSource="{Binding DataViews}"/>
Next create a DataTemplate for the DataView type. As each data view in the bound collection represents a data table that should be displayed as a DataGrid, it would look like this.
<DataTemplate DataType="{x:Type data:DataView}">
<DataGrid ItemsSource="{Binding}"/>
</DataTemplate>
Then you have to assign this data template as ItemTemplate in the ItemsControl. I just inline it here, but you can also put it in any ResourceDictionary and reference it via StaticResource.
<ItemsControl ItemsSource="{Binding DataViews}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type data:DataView}">
<DataGrid ItemsSource="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Now, you just have to add the default views to the DataViews collection. As this collection is an ObservableCollection, it will notify the ItemsControl to update on each add.
foreach (var dataTable in Results)
{
DataViews.Add(dataTable.DefaultView);
};
As an item is added, the ItemsControl will get notified and creates an item using the DataTemplate.
I recommend you to use the MVVM solution, as it separates the presentation from your data and is much easier to realize and customization of the DataGrids via the DataTemplate is much easier, convenient and maintainable in XAML.
<Page x:Class="Emergency_APP_Server_WPF.Forms.SQLPage"
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:z="clr-namespace:System.Data;assembly=System.Data"
xmlns:local="clr-namespace:Emergency_APP_Server_WPF.Forms"
mc:Ignorable="d" x:Name="SQLPageXaml" Loaded="SQLPage_Loaded"
d:DesignHeight="450" d:DesignWidth="800"
Title="SQLPage" >
<Grid>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" MinHeight="100"></RowDefinition>
<RowDefinition Height="0"></RowDefinition>
<RowDefinition Height="*" MinHeight="150"></RowDefinition>
<RowDefinition Height="35"></RowDefinition>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="70"></ColumnDefinition>
</Grid.ColumnDefinitions>
<avalonEdit:TextEditor Grid.Column="0" Background="White"
xmlns:avalonEdit="http://icsharpcode.net/sharpdevelop/avalonedit"
Name="MyAvalonEdit" FontFamily="Consolas"
FontSize="11pt" Margin="10,10,10,10" ShowLineNumbers="True"
LineNumbersForeground="Gray" ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto" KeyUp="MyAvalonEdit_KeyUp"/>
<Button Grid.Column="1" x:Name="BtnSendSQL" Margin="0,10,10,10" Click="BtnSendSQL_Click">
<StackPanel>
<Image Height="25" Source="..//Resources/send.png"></Image>
<TextBlock Margin="0,10,0,0" VerticalAlignment="Top" Foreground="White" Text="SendSQL"></TextBlock>
</StackPanel>
</Button>
</Grid>
<GridSplitter Margin="0,-10,0,0" Grid.Row="1" HorizontalAlignment="Stretch" Background="Transparent" ResizeBehavior="PreviousAndNext">
</GridSplitter>
<ScrollViewer Grid.Row="2">
<ItemsControl ItemsSource="{Binding DataViews}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type z:DataView}">
<DataGrid RowStyle="{StaticResource DataGridRowSql}" Style="{StaticResource DataGridStyleSQL}" ItemsSource="{Binding}">
<DataGrid.CommandBindings>
<CommandBinding Command="Copy" Executed="CommandBinding_Executed"></CommandBinding>
</DataGrid.CommandBindings>
<DataGrid.InputBindings>
<KeyBinding Key="C" Modifiers="Ctrl" Command="Copy"></KeyBinding>
</DataGrid.InputBindings>
</DataGrid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
<TextBlock TextAlignment="Center" TextWrapping="Wrap" VerticalAlignment="Bottom" Grid.Row="3" x:Name="TxtSqlLog" Text="Wait For Commands..."
FontSize="14" FontFamily="Consolas" Foreground="White"></TextBlock>
</Grid>
</Grid>
public ObservableCollection<DataView> _DataViews = new ObservableCollection<DataView>();
public ObservableCollection<DataView> DataViews
{
get
{
return _DataViews;
}
set
{
_DataViews = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string Param = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(Param));
}
foreach (SQLWindow SingleSQLWindows in StaticVar.MySQLWindows)
{
if (SingleSQLWindows.MyINDEX == index)
{
SingleSQLWindows.Dispatcher.Invoke(new Action(() =>
{
foreach (TabItem item in SingleSQLWindows._tabItems)
{
if (item.Name == TabName)
{
foreach (DataTable itemTable in Results)
{
//SingleSQLWindows.DataViews.Add(itemTable.DefaultView);
////create more datagrid while results.count
///
((SQLPage)((Frame)item.Content).Content).DataViews.Add(itemTable.DefaultView);
//((SQLPage)((Frame)item.Content).Content).TxtSqlLog.Text = "Records in Datagrid: " + Results[0].Rows.Count;//foreach
}
}
}
}));
}
}
the visual studio does not report any errors but the scrollviewer remains empty, I cannot understand why
I have a main window viewmodel that is creating an instance of the usercontrol, and in a specific function, setting a property of the usercontrol via code i.e.
// Set Button Width Height
var potentialHeight = (SelectorUCViewModel.GridHeight /
SelectorUCViewModel.ColumnCount) - (SelectorUCViewModel.ButtonMarginToUse * 2);
var potentialWidth = (SelectorUCViewModel.GridWidth /
SelectorUCViewModel.RowCount) - (SelectorUCViewModel.ButtonMarginToUse * 2);
SelectorUCViewModel.ButtonHeightWidth = (potentialHeight < potentialWidth) ? potentialHeight : potentialWidth;
When this is called, the properties in the usercontrol change (you can see the setter is called). However, on the UI it looks like nothing has happened (margin is not there... toggle buttons are not there...)
I don't understand why....
An extract of XAML of my viewmodel has the following:
<DockPanel LastChildFill="False">
<views:SelectorUC x:Name="UC" DataContext="{Binding SelectorUCViewModel}" />
<Border DockPanel.Dock="Right" Width="160" Margin="10 0 10 0">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Center">
Then the UserControl has the following XAML
<UserControl x:Class="SelectorUC"
x:Name="UC"
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:local="clr-namespace:Selector"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Name="mainGrid" DockPanel.Dock="Left" MouseDown="MainGrid_MouseDown" MouseUp="MainGrid_MouseUp" MouseMove="MainGrid_MouseMove" Background="Transparent"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
MinWidth="{Binding GridWidth}" Height="{Binding GridHeight}">
<ItemsControl x:Name="objItemControl" ItemsSource="{Binding ObjCompositeCollection}">
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Row" Value="{Binding Row}"/>
<Setter Property="Grid.Column" Value="{Binding Column}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid DockPanel.Dock="Top" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Name="grid" Grid.Row="1"
Width="{Binding MinWidth, ElementName=mainGrid}"
Height="{Binding Height, ElementName=mainGrid}"
GridHelper.RowCount="{Binding RowCount}"
GridHelper.ColumnCount="{Binding ColumnCount}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type ObjA}">
<ToggleButton Content="{Binding Id}"
Tag="{Binding Id}"
IsChecked="{Binding IsSelected}"
Height="{Binding Path=ButtonHeightWidth, ElementName=UC}"
Width="{Binding Path=ButtonHeightWidth, ElementName=UC}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Margin="{Binding Path=ButtonMarginToUse, ElementName=UC}"
Padding="2"
PreviewMouseLeftButtonDown="Button_PreviewMouseLeftButtonDown"
PreviewMouseLeftButtonUp="Button_PreviewMouseLeftButtonUp"
MouseEnter="Button_MouseEnter"
MouseLeave="Button_MouseLeave"
Style="{StaticResource ObjButton}">
</ToggleButton>
</DataTemplate>
<DataTemplate DataType="{x:Type GridLabeller}">
<TextBlock Text="{Binding HeaderName}" Style="{StaticResource GridHeaders}"/>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
<Canvas>
<Rectangle x:Name="selectionBox" Visibility="Collapsed" Stroke="{StaticResource ResourceKey=SecondaryThemeColour}" StrokeThickness="2" StrokeDashArray="2,1"/>
</Canvas>
</Grid>
</UserControl>
And the properties that are resetting in the UC class
private int buttonHeightWidth;
public int ButtonHeightWidth
{
get { return buttonHeightWidth; }
set
{
buttonHeightWidth = value;
OnPropertyChanged(nameof(ButtonHeightWidth));
}
}
private int buttonMarginToUse = 2;
public int ButtonMarginToUse
{
get { return buttonMarginToUse; }
set
{
buttonMarginToUse = value;
OnPropertyChanged(nameof(ButtonMarginToUse));
}
}
The mainwindow view sets the datacontext of the mainwindowviewmodel
public MainWindow()
{
InitializeComponent();
viewModel = new MainWindowViewModel(model);
DataContext = viewModel;
SourceInitialized += Window1_SourceInitialized;
}
I genuinely can't understand what is wrong - there is no binding error, but since shifting my code from viewmodel to usercontrol (was trying to abstract as much as i can from vm) things are not working properly...
In a UWP Project I have a ListView that is bound to an ObservableCollection<T> of Person objects. I use a DataTemplate to display the Person object within the ListView. The collection (people) can only contain up to a certain number of people set by _maxPeopleCount.
What I would like to happen is for the ListView to:
Not scroll at all.
Show ALL Person objects as defined by _maxPeopleCount.
Show each Person objects fully, not partially. That is, show each item so that ListViewItem.Height = (Available Height from ItemsPanel ) / _maxPeopleCount.
When there are less than _maxPeopleCount items to start with still show the added items with there Height as defined in (3).
Show the FirstName and LastName of each Person to be as large as possible.
At the moment I can't get my project to do this.
Here is the example code that shows the issue. Simply press the button more than 7 times, as this is the _maxPeopleCount in the code. You will see that 7 people do not get shown. Apologies for the awful UI. It's made to show problem with minimal XAML but be similar to my real project.
Code behind:
public sealed partial class MainPage : Page
{
ObservableCollection<Person> _people = new ObservableCollection<Person>();
int _personCount = 0;
int _maxPeopleCount = 7;
public MainPage()
{
this.InitializeComponent();
this.DataContext = _people;
}
private void btnAddPerson_Click(object sender, RoutedEventArgs e)
{
if (_people.Count == _maxPeopleCount)
{
_people.RemoveAt(_people.Count - 1);
}
_personCount += 1;
_people.Insert(0, new Person($"FirstName {_personCount}", $"LastName {_personCount}"));
}
XAML:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Grid.Row="0" Grid.Column="0" x:Name="btnAddPerson" Click="btnAddPerson_Click" Content="Add Person" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<ListView BorderBrush="Black" BorderThickness="5" Margin="10" Grid.Row="1" Grid.Column="0" ItemsSource="{Binding}" ScrollViewer.VerticalScrollMode="Disabled" ScrollViewer.VerticalScrollBarVisibility="Hidden" ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.HorizontalScrollMode="Disabled" VerticalAlignment="Stretch" VerticalContentAlignment="Stretch" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel HorizontalAlignment="Stretch"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Grid x:Name="grdPerson">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" Background="Green" HorizontalAlignment="Stretch">
<Viewbox>
<TextBlock Text="{Binding FirstName}" HorizontalAlignment="Stretch"/>
</Viewbox>
</Border>
<Border Grid.Column="1" Background="Yellow" HorizontalAlignment="Stretch">
<Viewbox>
<TextBlock Text="{Binding LastName}" HorizontalAlignment="Stretch" />
</Viewbox>
</Border>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
PERSON CLASS:
public class Person : INotifyPropertyChanged
{
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
private string _firstName = string.Empty;
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
OnPropertyChanged();
}
}
private string _lastName = string.Empty;
public string LastName
{
get { return _lastName; }
set
{
_lastName = value;
OnPropertyChanged();
}
}
protected void OnPropertyChanged([CallerMemberName] string caller = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(caller));
}
public event PropertyChangedEventHandler PropertyChanged;
}
Not scroll at all.
For this, you have already done by ScrollViewer.VerticalScrollMode="Disabled" ScrollViewer.VerticalScrollBarVisibility="Hidden".
Show ALL Person objects as defined by _maxPeopleCount.
Actually you have already bind _maxPeopleCount person records to the ListView, but restricted by the windows size you cannot see them all. The invisible records do exist there. If you stretch the application windows size to let the height be larger by manually dragging you will see the remaining records. If you want to keep all the records always be shown visible you may need to calculate the ListViewItem height by yourself.
Show each Person objects fully, not partially. That is, show each item so that ListViewItem.Height = (Available Height from ItemsPanel ) / _maxPeopleCount.
In your scenario, the height of ListViewItem is calculated automatically since you don't set a fix height for it. As the formula you showed here, we can dynamically bind the height of ListViewItem by ViewTreeHelper class in code behind. Pay attention that windows size change will influence the actual size of the ListView, so we also need to resize the ListViewItem when applicaiton window size changed. When the height of ListViewItem is setting to less than 44, it will not take effects since the default MinHeight of ListViewItem styles and templates is 44, which need to reset. More settings for implementing this requirement please reference the demo below.
When there are less than _maxPeopleCount items to start with still show the added items with there Height as defined in (3).
After calulated, all the ListViewItem will have the same height.
Show the FirstName and LastName of each Person to be as large as possible.
For this you have <Setter Property="HorizontalContentAlignment" Value="Stretch"/> to stretch the horizontal content, please also stretch the vertical content by <Setter Property="VerticalContentAlignment" Value="Stretch"/>. And padding of ListviewItem need to be set to 0.
Updated code as follows which can meet all your above requirements now.
XAML
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" x:Name="gridroot">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Grid.Row="0" Grid.Column="0" x:Name="btnAddPerson" Click="btnAddPerson_Click" Content="Add Person" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<ListView x:Name="listperson" BorderBrush="Black" BorderThickness="5" Margin="10" Grid.Row="1" Grid.Column="0" ItemsSource="{Binding}" ScrollViewer.VerticalScrollMode="Disabled" ScrollViewer.VerticalScrollBarVisibility="Hidden" ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.HorizontalScrollMode="Disabled" VerticalAlignment="Stretch" VerticalContentAlignment="Stretch" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="MinHeight" Value="20"/>
<Setter Property="Padding" Value="0"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel HorizontalAlignment="Stretch"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Grid x:Name="grdPerson" Loaded="grdPerson_Loaded" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" Background="Green" HorizontalAlignment="Stretch" >
<Viewbox>
<TextBlock Text="{Binding FirstName}" HorizontalAlignment="Stretch" />
</Viewbox>
</Border>
<Border Grid.Column="1" Background="Yellow" HorizontalAlignment="Stretch">
<Viewbox>
<TextBlock Text="{Binding LastName}" HorizontalAlignment="Stretch" />
</Viewbox>
</Border>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
Code behind
public sealed partial class MainPage : Page
{
ObservableCollection<Person> _people = new ObservableCollection<Person>();
int _personCount = 0;
int _maxPeopleCount = 7;
public MainPage()
{
this.InitializeComponent();
this.DataContext = _people;
Window.Current.SizeChanged += Current_SizeChanged;
}
public void resize()
{
var listpersonheight = listperson.ActualHeight;
IEnumerable<ListViewItem> items = FindVisualChildren<ListViewItem>(listperson);
for (int i = 0; i < items.Count(); i++)
{
foreach (ListViewItem item in items)
{
item.Height = (listpersonheight - 10) / _maxPeopleCount;// BorderThickness size need to be minus.
item.Width = listperson.ActualWidth - 10; //Width also need resize.
}
}
}
private void Current_SizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
{
resize();
}
private void grdPerson_Loaded(object sender, RoutedEventArgs e)
{
resize();
}
private void btnAddPerson_Click(object sender, RoutedEventArgs e)
{
if (_people.Count == _maxPeopleCount)
{
_people.RemoveAt(_people.Count - 1);
}
_personCount += 1;
_people.Insert(0, new Person($"FirstName {_personCount}", $"LastName {_personCount}"));
}
private static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
}
You could always write your own Panel, it's quite easy to do. Here's a UniformStackPanel I wrote which works like a StackPanel except it will stretch each item to fill the available space (regardless of how many items it has).
public class UniformStackPanel : StackPanel
{
protected override Size MeasureOverride(Size availableSize)
{
var childSize = Orientation == Orientation.Horizontal ?
new Size(availableSize.Width / Children.Count, availableSize.Height) :
new Size(availableSize.Width, availableSize.Height / Children.Count);
double alongAxis = 0;
double crossAxis = 0;
foreach (var child in Children)
{
child.Measure(childSize);
if (Orientation == Orientation.Horizontal)
{
alongAxis += child.DesiredSize.Width;
crossAxis = Math.Max(crossAxis, child.DesiredSize.Height);
}
else
{
alongAxis += child.DesiredSize.Height;
crossAxis = Math.Max(crossAxis, child.DesiredSize.Width);
}
}
return Orientation == Orientation.Horizontal ?
new Size(alongAxis, crossAxis) :
new Size(crossAxis, alongAxis);
}
protected override Size ArrangeOverride(Size finalSize)
{
var childSize = Orientation == Orientation.Horizontal ?
new Size(finalSize.Width / Children.Count, finalSize.Height) :
new Size(finalSize.Width, finalSize.Height / Children.Count);
double alongAxis = 0;
foreach (var child in Children)
{
if (Orientation == Orientation.Horizontal)
{
child.Arrange(new Rect(alongAxis, 0, childSize.Width, childSize.Height));
alongAxis += childSize.Width;
}
else
{
child.Arrange(new Rect(0, alongAxis, childSize.Width, childSize.Height));
alongAxis += childSize.Height;
}
}
return finalSize;
}
}
Use it with an ItemsControl like this:
<ItemsControl BorderBrush="Black" BorderThickness="5" Margin="10" Grid.Row="1" ItemsSource="{Binding}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<local:UniformStackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" Background="RoyalBlue" HorizontalAlignment="Stretch">
<Viewbox>
<TextBlock Text="{Binding FirstName}"/>
</Viewbox>
</Border>
<Border Grid.Column="1" Background="Crimson" HorizontalAlignment="Stretch">
<Viewbox>
<TextBlock Text="{Binding LastName}"/>
</Viewbox>
</Border>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Ok, I think I've cracked it! I changed the ItemsPanelTemplate to a UniformGrid panel that I got from here . Its a UWP version for the one that WPF has, and as such you can set the number of rows and columns. for my example above, Rows = 7 , Columns = 1.
All I changed after that was to add the following additional Setter in the ItemContainerStyle:
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
which fixed the issue of the ListViewItems content not being maximized.
Hope this helps others, and cheers to those who replied with help.
I checked various links here and the web that dealt with various data grids not showing data but I did not come across one that had a similar scenario as the problem I am having.
Not refreshing in WPF with DataGrid
WPF Does not show data inside datagrid
How to get data into datagrid using dataset?
DataGridView doesn't show data: DataSet is empty
In C# WPF I have created an application that when run, it comes up to a main menu screen with tabs to user pages and different buttons at the top of the page such has: home, exit, update and settings. The problem I am having is in my settings menu. When the user clicks on the settings button from the MainWindow a SettingsMainWindow pops up. In the white section are links that the user clicks on to get to different settings pages and the window to the right slides to the next page depending on what is clicked.
SettingsMainWindow.xaml
``
<!-- Define supported transitions -->
<NavigationTransition:NavigationCubeTransition x:Key="CubeTransition" Duration="0:0:0.8"/>
<NavigationTransition:NavigationSlideTransition x:Key="SlideTransition" Duration="0:0:0.8"/>
<NavigationTransition:NavigationFlipTransition x:Key="FlipTransition" Duration="0:0:0.8"/>
<BooleanToVisibilityConverter x:Key="bvc"/>
</Window.Resources>
<DockPanel Grid.RowSpan="5" Height="auto" HorizontalAlignment="Stretch" Name="dockPanelLogin" VerticalAlignment="Stretch" Width="auto" Opacity="10" Grid.ColumnSpan="2" OpacityMask="{DynamicResource {x:Static SystemColors.MenuTextBrushKey}}" >
<DockPanel.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black" Offset="0"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</DockPanel.Background>
<Grid Margin="100">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="3*"/>
</Grid.ColumnDefinitions>
<DockPanel Grid.Column="0" LastChildFill="True" Margin="5">
<!-- Each ComboBoxItem contains the transition in its Tag property
so that the TransitionContainer can directly databind to the
selected transition -->
<ComboBox x:Name="transitionComboBox" SelectedIndex="0" DockPanel.Dock="Bottom" Margin="0,3,0,0">
<!--<ComboBoxItem Tag="{StaticResource CubeTransition}">Cube</ComboBoxItem>-->
<ComboBoxItem Tag="{StaticResource SlideTransition}">Slide</ComboBoxItem>
<!--<ComboBoxItem Tag="{StaticResource FlipTransition}">Flip</ComboBoxItem>-->
</ComboBox>
<ListBox Grid.Column="0"
ItemsSource="{Binding Views}" IsSynchronizedWithCurrentItem="True"
SelectedIndex="0">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" Padding="10"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
<Border Grid.Column="1" Margin="5" BorderBrush="#FF7F9DB9" BorderThickness="1">
<!-- The ContentProperty is not bound directly to the SelectedItem of the ListBox because the
GoForward property must be updated BEFORE the content changes. The CurrentContent property
is defined in the ViewModel class and updated everytime the selection of the ListBox changes,
after setting up the GoForward value. -->
<NavigationTransition:NavigationPresenter
Content="{Binding CurrentContent}"
Transition="{Binding ElementName=transitionComboBox, Path=SelectedItem.Tag}"
GoForward="{Binding DataContext.GoForward, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type NavigationTransition:NavigationSlideTransition}}}"/>
</Border>
<!--Region Editor-->
<Grid Grid.RowSpan="100" Grid.ColumnSpan="100"
Background="#88000000" Visibility="{Binding IsShowingPage3View, Converter={StaticResource bvc}, FallbackValue=Collapsed}">
<View:Page3View Margin="0, 200, 0, 0"
x:Name="page3View" DataContext="{Binding}" Width="800" Height="700"/>
</Grid>
</Grid>
</DockPanel>
My issue is showing data from my Access DB in the data grid on the page below. This grid should be auto populated with the regions table information upon load, where the user can add additional regional information. There should be four columns showing in the below when run. I have implemented this same feature elsewhere in my application that is not in a menu and it works just fine. When in debug it shows that it is reading the table properly but it’s just not showing nor giving any errors. My question is how to get this to show the required data while being within a menu page.
Page3View.xaml
<Border BorderBrush="{DynamicResource {x:Static SystemColors.HotTrackBrushKey}}"
BorderThickness="1"
Background="{DynamicResource {x:Static SystemColors.GradientActiveCaptionBrushKey}}"
Padding="20">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="1">
<Rectangle HorizontalAlignment="Left" Width="100" Height="6" Fill="{DynamicResource {x:Static SystemColors.MenuHighlightBrushKey}}" />
<TextBlock FontSize="24" Foreground="#444444" FontWeight="Bold" Text="REGION-DISTRICT NAME EDITOR" />
</StackPanel>
<StackPanel Grid.Row="1" Height="10"/>
<DataGrid x:Name="dg" Grid.Row="2" Height="Auto" VerticalAlignment="Stretch"
VirtualizingStackPanel.IsVirtualizing="True"
CanUserAddRows="True"
CanUserDeleteRows="True"
CanUserResizeRows="False"
CanUserReorderColumns="False"
CanUserResizeColumns="True"
CanUserSortColumns="False"
VirtualizingStackPanel.VirtualizationMode="Recycling"
AutoGenerateColumns="True">
</DataGrid>
<!--<StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Right" Grid.ColumnSpan="4"
IsEnabled="{Binding TaskbarProgressValue, Converter={StaticResource ProgressBarStartButtonEnabledConverter}}">
<Button Style="{StaticResource PanelCloseButtonStyle}" Margin="0" Click="btnClose_Click" />
</StackPanel>-->
<StackPanel Margin="0, 10, 0, 0" Grid.Row="4" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Height="Auto" Click="btnSubmit_Click" Content="OK" Command="{Binding ToggleRegionEditorsCommand}" CommandParameter="{StaticResource False}" Width="90"/>
<StackPanel Width="10" />
<Button Height="Auto" Click="btnClose_Click" Content="Cancel" Command="{Binding ToggleRegionEditorsCommand}" CommandParameter="{StaticResource False}" Width="90" />
</StackPanel>
<!--Added for page 3 region editor-->
<!--<Grid Grid.RowSpan="100" Grid.ColumnSpan="100"
Background="#88000000" Visibility="{Binding IsShowingPage3View, Converter={StaticResource bvc}, FallbackValue=Collapsed}">
<View:Page3View Margin="0, 200, 0, 0"
x:Name="page3View" DataContext="{Binding}" Width="800" Height="700"/>
</Grid>-->
</Grid>
</Border>
Page3View.xaml.cs
{
/// <summary>
/// Interaction logic for Page3View.xaml Region Distric Editor
/// </summary>
public partial class Page3View : UserControl
{
public Page3View()
{
InitializeComponent();
}
private IDbDriver Database { get; set; }
private DataHelper DataHelper { get; set; }
public void Init(DataHelper dataHelper)
{
if (dg.ItemsSource != null)
{
dg.ItemsSource = null;
}
Database = dataHelper.Database;
DataHelper = dataHelper;
Query selectQuery = Database.CreateQuery("SELECT DRS, RegCode, DISTRICT, DistCode FROM [codedistrict1] ORDER BY DRS, DISTRICT");
DataTable dt = Database.Select(selectQuery);
dg.ItemsSource = (dt).DefaultView;
}
private void btnSubmit_Click(object sender, RoutedEventArgs e)
{
if (dg.ItemsSource != null)
{
DataView dv = dg.ItemsSource as DataView;
if (dv != null && dv.Table != null)
{
DataTable dt = dv.Table;
if (dt != null && Database != null)
{
try
{
string querySyntax = "DELETE * FROM [codedistrict1]";
if (Database.ToString().ToLower().Contains("sql"))
{
querySyntax = "DELETE FROM [codedistrict1]";
}
Query deleteQuery = Database.CreateQuery(querySyntax);
Database.ExecuteNonQuery(deleteQuery);
DataRow[] rows = dt.Select(String.Empty, String.Empty, DataViewRowState.CurrentRows);
foreach (DataRow row in rows)
{
Query insertQuery = Database.CreateQuery("INSERT INTO [codedistrict1] (DRS, RegCode, DISTRICT, DistCode) VALUES (" +
"#DRS, #RegCode, #DISTRICT, #DistCode)");
insertQuery.Parameters.Add(new QueryParameter("#DRS", DbType.String, row[0].ToString()));
insertQuery.Parameters.Add(new QueryParameter("#RegCode", DbType.String, row[1].ToString()));
insertQuery.Parameters.Add(new QueryParameter("#DISTRICT", DbType.String, row[2].ToString()));
insertQuery.Parameters.Add(new QueryParameter("#DistCode", DbType.String, row[3].ToString()));
Database.ExecuteNonQuery(insertQuery);
}
MessageBox.Show("Changes were committed to the database successfully.", "Success", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
MessageBox.Show("Changes were not committed to the database successfully. Error: " + ex.Message, "Failure", MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
}
}
}
}
}
private void btnClose_Click(object sender, RoutedEventArgs e)
{
}
}
}
DataHelper.cs
public class DataHelper : DataHelperBase
{
#region Members
private bool _isShowingPage3View = false;
#endregion
#region Properties
// Shows the region districts update page for the settings page
public bool IsShowingPage3View
{
get
{
return this._isShowingPage3View;
}
set
{
if (!IsShowingPage3View == value)
{
this._isShowingPage3View = value;
RaisePropertyChanged("IsShowingPage3View");
}
}
}
// This is for the region district editor on the settings page
public ICommand TogglePage3ViewCommand { get { return new RelayCommand<bool> TogglePage3ViewCommandExecute); } }
private void TogglePage3ViewCommandExecute(bool show)
{
IsShowingPage3View = show;
}
private void btnSettings_Click(object sender, RoutedEventArgs e)
{
{
Regions.View.Page3View.Int(this.DataHelper);
}
}
With the above code I am now getting an error that it does not contain a definition for Int although it is defined on Page3View. It should be tied to this block of code.
public void Init(DataHelper dataHelper)
{
if (dg.ItemsSource != null)
{
dg.ItemsSource = null;
}
Database = dataHelper.Database;
DataHelper = dataHelper;
Query selectQuery = Database.CreateQuery("SELECT DRS, RegCode, DISTRICT, DistCode FROM [codedistrict1] ORDER BY DRS, DISTRICT");
DataTable dt = Database.Select(selectQuery);
dg.ItemsSource = (dt).DefaultView;
}
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;
}