Wpf dynamic design with Grid and data binding - wpf

I am creating dynamic UI using from code.I am adding grid to the Page using for loop,in multiple rows and 4 columns,so it takes time to load UI on Page.I want to load each child Grid lazily, I don't know how to do it, please help.
here is XAML file
<ScrollViewer Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" CanContentScroll="True" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Margin="0 0 0 0">
<Grid x:Name="mainGrid" HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="10" Width="Auto">
</Grid>
</ScrollViewer>
and this is pseudo code from .cs file
for (int column = 0; column < dsMacDtls.Tables[0].Rows.Count; column++)
{
grid = new Grid();
grid.Width = 108;
grid.Height = 108;
if (column % 4 == 0)
{
mainGrid.RowDefinitions.Add(new RowDefinition());
}
else
{
mainGrid.RowDefinitions.Add(new RowDefinition());
}
box = new CheckBox();
TextBlock tb = new TextBlock();
tb.Text = "abcd";
box.Content = tb;
grid.Children.Add(box);
ImageBrush myBrush = new ImageBrush();
image = new Image();
image.Source = new BitmapImage(new Uri("abcd.png"));
grid.Background = myBrush;
Grid.SetColumn(grid, 0);
Grid.SetRow(grid, i);
Border br = new Border();
br.BorderThickness = new Thickness(1);
br.BorderBrush = new SolidColorBrush(Colors.Gray);
Grid.SetColumn(br, 0);
Grid.SetRow(br, rowNumberofMachine);
br.Width = 108;
br.Height = 108;
grid.Children.Add(br);
mainGrid.Children.Add(grid);
}

Related

WPF ScrollViewer not working in Grid programmatically populated

I have this situation in xaml
<StackPanel Orientation="Vertical" Margin="5" Background="AliceBlue" Height="370">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<Grid Name="DescriptionsGrid" MinHeight="0" MaxHeight="370" ScrollViewer.CanContentScroll="True"></Grid>
</ScrollViewer>
</StackPanel>
and I'm filling the Grid with the following
DescriptionsGrid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
TextBlock textBlock = new TextBlock { Text = description, TextWrapping = TextWrapping.WrapWithOverflow };
Label label = new Label { Content = textBlock, Foreground = new SolidColorBrush(Colors.Blue) };
label.MouseLeftButtonDown += new MouseButtonEventHandler(LoadXML);
DescriptionsGrid.Children.Add(label);
Grid.SetRow(DescriptionsGrid.Children[DescriptionsGrid.Children.Count - 1], description_counter);
description_counter++;
I can't define rows height as the description may be long enought to wrap the text to new line.
The scrollbar does not appears and new elements go hidden down below.
Any idea?
Following the suggestion of #Clemens I rewrote the code, and found that the mistake was in the XML.
To populate the grid
foreach (HostMessages hostMessage in hostMessagesList)
{
Label message = new Label
{
Content = new TextBlock {
Text = hostMessage.Description,
TextWrapping = TextWrapping.WrapWithOverflow
},
Foreground = new SolidColorBrush(Colors.Blue),
Cursor = Cursors.Hand
};
message.MouseLeftButtonDown += new MouseButtonEventHandler(LoadXML);
itemsControl.Items.Add(message);
}
The XML is as follows
<DockPanel Grid.Row="1" Margin="5" Background="AliceBlue" Height="370">
<ScrollViewer VerticalScrollBarVisibility="Auto" Height="370">
<ItemsControl Name="itemsControl"></ItemsControl>
</ScrollViewer>
</DockPanel>
The mistake was assigning a MaxHeight="370" to the Grid and none to the ScrollViewer.

Generate a complex grid in WPF programmatically

I have working complex grid in wpf written in XAML.
My purpose is to convert the XAML code to c#, so I am going to be able to generate this grid programmatically.
This is the working XAML code
<Grid>
<!--<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="2*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding ElementName=datagrid1, Path=RowHeaderWidth}" />
<ColumnDefinition Width="{Binding ElementName=Column1, Path=ActualWidth}" />
<ColumnDefinition Width="{Binding ElementName=Column2, Path=ActualWidth}" />
<ColumnDefinition Width="{Binding ElementName=Column3, Path=ActualWidth}" />
<ColumnDefinition Width="{Binding ElementName=Column4, Path=ActualWidth}" />
</Grid.ColumnDefinitions>
<Border Grid.Column="1" Grid.ColumnSpan="4" HorizontalAlignment="Stretch" BorderThickness="2">
<Label HorizontalAlignment="Center">Super Header!!</Label>
</Border>
</Grid>
<DataGrid AutoGenerateColumns="False"
Name="datagrid1" Grid.Row="1" Width="10">
<DataGrid.Columns>
<DataGridTextColumn Header="column 1" Width="100" x:Name="Column1" />
<DataGridTextColumn Header="column 2" Width="80" x:Name="Column2"/>
<DataGridTextColumn Header="column 3" Width="80" x:Name="Column3"/>
<DataGridTextColumn Header="column 4" Width="*" x:Name="Column4"/>
</DataGrid.Columns>
</DataGrid>-->
When I compile it shows the desire result which looks like
this: http://i.imgur.com/BK8GDtd.png?1
What I have tried is this :
Grid outernGrid = new Grid();
outernGrid.ShowGridLines = true;
RowDefinition row1 = new RowDefinition();
row1.Height = GridLength.Auto;
RowDefinition row2 = new RowDefinition();
row2.Height = new GridLength(2, GridUnitType.Star);
outernGrid.RowDefinitions.Add(row1);
outernGrid.RowDefinitions.Add(row2);
//Second grid
Grid innerGrid = new Grid();
ColumnDefinition col0 = new ColumnDefinition();
ColumnDefinition col1 = new ColumnDefinition();
ColumnDefinition col2 = new ColumnDefinition();
ColumnDefinition col3 = new ColumnDefinition();
ColumnDefinition col4 = new ColumnDefinition();
//Binding Initialize
Binding b0 = new Binding { ElementName = "dataGrid2", Path = new PropertyPath("RowHeaderWidth") };
Binding b1 = new Binding { ElementName = "Column1", Path = new PropertyPath("ActualWidth") };
Binding b2 = new Binding { ElementName = "Column2", Path = new PropertyPath("ActualWidth") };
Binding b3 = new Binding { ElementName = "Column3", Path = new PropertyPath("ActualWidth") };
Binding b4 = new Binding { ElementName = "Column4", Path = new PropertyPath("ActualWidth") };
//Set binding
BindingOperations.SetBinding(col1, DataGrid.ColumnWidthProperty, b1);
BindingOperations.SetBinding(col2, DataGrid.ColumnWidthProperty, b2);
BindingOperations.SetBinding(col3, DataGrid.ColumnWidthProperty, b3);
BindingOperations.SetBinding(col4, DataGrid.ColumnWidthProperty, b4);
BindingOperations.SetBinding(col0, DataGrid.ColumnWidthProperty, b0);
//Adding columns
innerGrid.ColumnDefinitions.Add(col0);
innerGrid.ColumnDefinitions.Add(col1);
innerGrid.ColumnDefinitions.Add(col2);
innerGrid.ColumnDefinitions.Add(col3);
innerGrid.ColumnDefinitions.Add(col4);
Label header = new Label();
header.Content = "This is the superheader";
//Datagrid
DataGrid dataGrid = new DataGrid { Name = "dataGrid2" };
dataGrid.AutoGenerateColumns = false;
dataGrid.RowHeaderWidth = 10;
//datagrid columns
DataGridTextColumn datacol1 = new DataGridTextColumn();
DataGridTextColumn datacol2 = new DataGridTextColumn();
DataGridTextColumn datacol3 = new DataGridTextColumn();
DataGridTextColumn datacol4 = new DataGridTextColumn();
datacol1.Header = "Column1";
datacol2.Header = "Column2";
datacol3.Header = "Column3";
datacol4.Header = "Column4";
datacol1.Width = 100;
datacol1.Width = 80;
datacol1.Width = 80;
datacol1.Width = DataGridLength.Auto;
Grid.SetColumn(header,1);
header.SetValue(Grid.ColumnSpanProperty,4);
Grid.SetRow(innerGrid, 0);
Grid.SetColumn(dataGrid, 1);
outernGrid.Children.Add(innerGrid);
outernGrid.Children.Add(dataGrid);
RootWindow.Content = outernGrid;
}
And the output is this
Can someone help me solving this issue.
I have two observations for you while I attempt to work on this.
First in your code sample you have this:
datacol1.Width = 100;
datacol1.Width = 80;
datacol1.Width = 80;
datacol1.Width = DataGridLength.Auto;
I imagine you wanted this:
datacol1.Width = 100;
datacol2.Width = 80;
datacol3.Width = 80;
datacol4.Width = DataGridLength.Auto;
Second, you do not name your columns in the Datagrid programmatically.
You'll have to do something like this for each column:
datacol1.ColumnName = "Column1";
datacol2.ColumnName = "Column2";
datacol3.ColumnName = "Column3";
datacol4.ColumnName = "Column4";

Silverlight Button Event Not firing

I know this type of question has been asked before, But I have tried the suggestions to no avail, so hopefully some fresh eyes can help me on this.
I have a Custom Control, which is basically a Border with a grid inside which contains 2 textboxes and a button.
The control also has a octagon that is added in codebehind.
The button's click event does not fire. I gather it has probably got something to do with the controls above it getting the click event instead of the Button, but I don't know how to solve it. Or perhaps because the octagon is drawn afterwards in the code behind.
I have tried so many different things, moving the button, adding another grid, setting the background to Transparent. This is driving me nuts.
Below is the code.
XAML
<UserControl x:Class="HSCGym.UserControls.CustomErrorControl"
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="800" d:DesignWidth="1000">
<Border x:Name="ErrorBorder" BorderBrush="Black" BorderThickness="2" CornerRadius="20" Background="White"
Width="900" Height="700"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
Grid.Row="0" Grid.RowSpan="2">
<Border.Effect>
<DropShadowEffect BlurRadius="7" Direction="300" ShadowDepth="6" Color="Black" />
</Border.Effect>
<Grid x:Name="LayoutRoot" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Transparent" >
<Grid.RowDefinitions>
<RowDefinition Height="55" />
<RowDefinition Height="55" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding ErrorText}" HorizontalAlignment="Center" FontSize="40" Grid.Row="0"/>
<TextBlock Text="{Binding ErrorText2}" HorizontalAlignment="Center" FontSize="40" Grid.Row="1"/>
<Button x:Name="btnExit" VerticalAlignment="Bottom" HorizontalAlignment="Left" Margin="38,0,0,20"
Content="Exit" Height="50" Width="200" FontSize="20" Click="btnExit_Click"
Grid.Row="2"/>
</Grid>
</Border>
</UserControl>
And the Code Behind:
public partial class CustomErrorControl
{
public string ErrorText { get; set; }
public string ErrorText2 { get; set; }
public string StopText { get; set; }
private readonly int x;
private readonly int y;
private readonly int r;
public CustomErrorControl(int x, int y, int radius, string stopError)
{
InitializeComponent();
this.x = x;
this.y = y;
r = radius;
StopText = stopError;
DrawOctagon(this.x, this.y, r);
}
public void DrawOctagon(int x, int y, int R)
{
int r2 = (int)(R/Math.Sqrt(2));
// OuterOctagon
Point[] outerOctagon = new Point[8];
outerOctagon[0].X = x;
outerOctagon[0].Y = y - R;
outerOctagon[1].X = x + r2;
outerOctagon[1].Y = y - r2;
outerOctagon[2].X = x + R;
outerOctagon[2].Y = y;
outerOctagon[3].X = x + r2;
outerOctagon[3].Y = y + r2;
outerOctagon[4].X = x;
outerOctagon[4].Y = y + R;
outerOctagon[5].X = x - r2;
outerOctagon[5].Y = y + r2;
outerOctagon[6].X = x - R;
outerOctagon[6].Y = y;
outerOctagon[7].X = x - r2;
outerOctagon[7].Y = y - r2;
HexColor stop1Colour = new HexColor("#FFA30D0D");
HexColor stop2Colour = new HexColor("#FFCA0C0C");
HexColor stop3Colour = new HexColor("#FFF71212");
GradientStop gs1 = new GradientStop { Color = stop1Colour, Offset = 0.98 };
GradientStop gs2 = new GradientStop { Color = stop2Colour, Offset = 0.5 };
GradientStop gs3 = new GradientStop { Color = stop3Colour, Offset = 0.04 };
LinearGradientBrush gb = new LinearGradientBrush
{
StartPoint = new Point(0.77, 0.85),
EndPoint = new Point(0.13, 0.15),
GradientStops = new GradientStopCollection {gs1, gs2, gs3}
};
DropShadowEffect dse = new DropShadowEffect
{
BlurRadius = 8,
Color = Colors.Black,
Direction = 320,
ShadowDepth = 10.0,
Opacity = 0.5
};
Polygon octagonOuter = new Polygon
{
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center,
Fill = gb,
Stroke = new SolidColorBrush(Colors.Black),
StrokeThickness = 5,
StrokeDashArray = new DoubleCollection { 10, 0 },
Effect = dse,
Points = new PointCollection
{
new Point(outerOctagon[0].X, outerOctagon[0].Y),
new Point(outerOctagon[1].X, outerOctagon[1].Y),
new Point(outerOctagon[2].X, outerOctagon[2].Y),
new Point(outerOctagon[3].X, outerOctagon[3].Y),
new Point(outerOctagon[4].X, outerOctagon[4].Y),
new Point(outerOctagon[5].X, outerOctagon[5].Y),
new Point(outerOctagon[6].X, outerOctagon[6].Y),
new Point(outerOctagon[7].X, outerOctagon[7].Y),
}
};
double outerOctCenterY = octagonOuter.Points[6].Y - octagonOuter.Points[2].Y;
double outerOctCenterX = octagonOuter.Points[4].X - octagonOuter.Points[0].X;
var rotate = new RotateTransform { Angle = 22.8, CenterX = outerOctCenterX, CenterY = outerOctCenterY };
octagonOuter.RenderTransform = rotate;
Grid.SetRow(octagonOuter, 2);
TextBlock tbStopError = new TextBlock
{
Text = StopText,
Foreground = new SolidColorBrush(Colors.White),
FontFamily = new FontFamily("Courier New"),
FontSize = 80,
FontWeight = FontWeights.Bold,
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center
};
Grid.SetRow(tbStopError, 2);
LayoutRoot.Children.Add(octagonOuter);
LayoutRoot.Children.Add(tbStopError);
}
private void btnExit_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Clicked");
}
}
Any ideas?
Many thanks
Neill
Update:
I have tested quickly putting MouseLeftButtonUp events on the grid and the border.
When I click the border, first the grid event fires, then the border event fires, but if I click the button, nothing fires. Does this mean the Button is above the grid and border so should be firing the event?
I will try some of the other suggestions as well in the meant time.
Neill
Update 2:
It gets stranger. I moved the button to the top and then click event fires. What I see is that from a certain Y point and below, absolutely no mouseleftbuttonup or click events fire, nada. This makes no sense. If I go from the bottom and click moving slowly up, then at a certain point, all events start firing.
Neill
Update 3
Hi all,
After removing controls one by one to see what is causing the issue, I now know it is the Border control. If I remove it, all works fine, when it is back, same problem as before. Any ideas?
Thanks
Neill
Update 4
Hi all,
OK so I finally sorted out the problem, so in case anybody finds themselves in a similar situation. It had nothing to do with that page at all. I am loading the page in a frame from a different page and what it seems is that I had to many row definitions set in the grid on the main page. After I corrected that, everything works fine.
Thanks
First try fill button by Transparent color (00000000).
Also you can send solution or part of it to me and I shall try to help.

How to set multiple canvas in scroll viewer in wpf?

I want to set an image in a canvas control and have 20 canvases that I am creating by using a loop. The problem is that when I want to add all those canvas items in to a scroll viewer it does not work. Here is my code:
private void CreateAndShowCanvas()
{
List<Canvas> list = new List<Canvas>();
for (int i = 0; i < 20; i++)
{
Canvas myCanvas1 = new Canvas();
myCanvas1.Background = new SolidColorBrush(Colors.Transparent);
myCanvas1.Height = 235;
myCanvas1.Width = 626;
//Canvas.SetZIndex(myCanvas1, 4);
Image MainImage = new Image();
MainImage.Width = 275;
MainImage.Height = 235;
BitmapImage mi = new BitmapImage(new Uri("select_1.png", UriKind.Relative));
MainImage.Source = mi;
Canvas.SetTop(MainImage, 0);
Canvas.SetLeft(MainImage, 0);
myCanvas1.Children.Add(MainImage);
Image SeparatorImage = new Image();
BitmapImage si = new BitmapImage(new Uri("Sentre Seprator.png", UriKind.Relative));
SeparatorImage.Height = 270;
SeparatorImage.Source = si;
Canvas.SetTop(SeparatorImage, -5);
Canvas.SetLeft(SeparatorImage, 310);
myCanvas1.Children.Add(SeparatorImage);
Image SecondImage = new Image();
SecondImage.Width = 275;
SecondImage.Height = 235;
BitmapImage sci = new BitmapImage(new Uri("select_2.png", UriKind.Relative));
SecondImage.Source = sci;
Canvas.SetTop(SecondImage, 0);
Canvas.SetLeft(SecondImage, 350);
myCanvas1.Children.Add(SecondImage);
list.Add(myCanvas1);
}
scv.Content = list;
}
and in XML:
<Window 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="350" Width="525">
<Grid>
<ScrollViewer Name="scv" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled" Margin="60,40,59,46" Opacity="99" Background="Transparent" />
</Grid>
</Window>
and when I run it, it only shows ("Collection"). Please help me out, thanks in advance...
Should you not just be using a ListBox with an ItemTemplate that has Canvases inside it instead? There must be an easier way to do whatever you are trying to achieve than creating 20 canvases manually.
Here's some reading to do with ListBoxes.
A ScrollViewer can hold just a single child, so you need another panel type like a Grid or StackPanel which holds your canvases.
<Grid>
<ScrollViewer Name="scv" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled" Margin="60,40,59,46" Opacity="99" Background="Transparent">
<StackPanel Name="stp" />
</ScrollViewer>
</Grid>
Add canvases to stp instead.

Using code behind only add button to dynamically generated WPF DataGrid column's header

I have a wpf datagrid that has it's columns generated dynamically in code and I need to insert small buttons in each column's header to the right of the text to implement custom (complex) filtering in a popup dialog.
I'm having trouble figuring out how to insert a button into a datagrid column header using only code behind.
This is the route I started going down (commented out bit) but it doesn't work:
private static DataGridTextColumn GetTextColumn(string ColumnName, string FormatString, bool AlignRight)
{
DataGridTextColumn c = new DataGridTextColumn();
c.Header = Test.Common.UIBizObjectCache.LocalizedText.GetLocalizedText(ColumnName);
c.Binding = new System.Windows.Data.Binding(ColumnName);
if (!string.IsNullOrWhiteSpace(FormatString))
c.Binding.StringFormat = FormatString;
if (AlignRight)
{
Style cellRightAlignedStyle = new Style(typeof(DataGridCell));
cellRightAlignedStyle.Setters.Add(new Setter(DataGridCell.HorizontalAlignmentProperty, HorizontalAlignment.Right));
c.CellStyle = cellRightAlignedStyle;
}
//var buttonTemplate = new FrameworkElementFactory(typeof(Button));
//buttonTemplate.Text = "X";
//buttonTemplate.AddHandler(
// Button.ClickEvent,
// new RoutedEventHandler((o, e) => HandleColumnHeaderButtonClick(o, e))
// );
//c.HeaderTemplate=new DataTemplate(){VisualTree = buttonTemplate};
return c;
}
I get an invalidoperationexception "'ContentPresenter' type must implement IAddChild to be used in FrameworkElementFactory AppendChild."
Clearly I'm doing it wrong. :) Any help would be greatly appreciated.
Do you need to use a template? If not use the normal Header property:
string colProperty = "Name";
DataGridTextColumn col = new DataGridTextColumn();
col.Binding = new Binding(colProperty);
var spHeader = new StackPanel() { Orientation = Orientation.Horizontal };
spHeader.Children.Add(new TextBlock(new Run(colProperty)));
var button = new Button();
button.Click += Button_Filter_Click;
button.Content = "Filter";
spHeader.Children.Add(button);
col.Header = spHeader;
dataGrid.Columns.Add(col);
For create a column header with an image button, you can do this in xaml :
<Window.Resources>
<BitmapImage x:Key="Img" UriSource="/Img/yourImage.png"/>
</Window.Resources>
<Datagrid Name="yourDatagrid">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button x:Name="Btn" Click="Btn_Click" >
<DockPanel>
<Image Source="{StaticResource ResourceKey=Img}" Height="16" Width="16"/>
</DockPanel>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</Datagrid>
Or you can autogenerate this in code behind like this :
Xaml :
<Window.Resources>
<BitmapImage x:Key="Img" UriSource="/Img/yourImage.png"/>
</Window.Resources>
C# :
Datagrid yourDatagrid = new Datagrid();
DataGridTemplateColumn colBtn = new DataGridTemplateColumn();
DataTemplate DttBtn = new DataTemplate();
FrameworkElementFactory btn = new FrameworkElementFactory(typeof(Button));
FrameworkElementFactory panel = new FrameworkElementFactory(typeof(DockPanel));
FrameworkElementFactory img = new FrameworkElementFactory(typeof(Image));
img.SetValue(Image.SourceProperty, (BitmapImage)FindResource("Img"));
img.SetValue(Image.HeightProperty, Convert.ToDouble(16));
img.SetValue(Image.WidthProperty, Convert.ToDouble(16));
panel.AppendChild(img);
btn.AppendChild(panel);
btn.AddHandler(Button.ClickEvent, new RoutedEventHandler(Btn_Click));
DttBtn.VisualTree = btn;
colBtn.CellTemplate = DttBtn;
yourDatagrid.Columns.Add(colBtn);

Resources