TextBlock.Inlines - add horizontal line - wpf

Below I am adding log entries divided by a horizontal line
Dim vLogText = vLog.Split("^")
Dim vRows As Integer = vLogText.Length - 1
For i As Integer = 0 To vRows
Dim Subrow As String = vLogText(i)
LogTB.Inlines.Add(Subrow)
LogTB.Inlines.Add(New Line With {.X1 = 0, .Y1 = 0, .X2 = 300, .Y2 = 0, .Stroke = New SolidColorBrush(Colors.Gray), .StrokeThickness = 4.0})
Next
This is OK if I want a preset length (say 300 in the above example) - but if it needs to stretch to the entire width of the container how is that accomplished?
Thanks
Added in reply to answer supplied by Anjum
Here is how the grid is added...
#Region "Right Grid"
Private Function RightGrid() As Grid
Try
Dim MainGrid As New Grid
Dim vGrid As New Grid
Dim SV As New ScrollViewer
With SV
.Name = "RightGrid_SV"
.Content = vGrid
.VerticalScrollBarVisibility = ScrollBarVisibility.Auto
End With
RegisterControl(WorkOrder_Grid, SV)
MainGrid.Children.Add(SV)
'Add in the status and log
Dim LogLB As New Label
With LogLB
.Name = "WorkOrder_LogLB"
End With
RegisterControl(WorkOrder_Grid, LogLB)
vGrid.Children.Add(LogLB)
If IsNewRecord = True Then
'Add some help data
Dim SP As New StackPanel
Dim HeaderTB As New TextBlock
With HeaderTB
.Text = "ADDING A NEW WORK ORDER" & Environment.NewLine
.HorizontalAlignment = HorizontalAlignment.Center
.FontWeight = FontWeights.Bold
End With
Dim DatesHeaderTB As New TextBlock
With DatesHeaderTB
.Text = "Dates"
.TextDecorations = TextDecorations.Underline
End With
Dim DatesContentTB As New TextBlock
With DatesContentTB
.Text = "Enter the Work Order date and the date the Work needs to be completed by." & Environment.NewLine
.TextWrapping = TextWrapping.Wrap
End With
Dim UnitHeaderTB As New TextBlock
With UnitHeaderTB
.Text = "Unit/Common Area"
.TextDecorations = TextDecorations.Underline
End With
Dim vUnit As String = "If the Work Order relates to a homeowners property, insert the details using the button. "
vUnit += "If the homeowners have a registered account they will be updated by email each time the Work Order status is changed!" & Environment.NewLine & Environment.NewLine
vUnit += "If the Work Order relates to a common area (e.g. Recreation grounds, Clubhouse...) just enter a short description of that area." & Environment.NewLine
Dim UnitContentTB As New TextBlock
With UnitContentTB
.Text = vUnit
.TextWrapping = TextWrapping.Wrap
End With
Dim TypeHeaderTB As New TextBlock
With TypeHeaderTB
.Text = "Work Type"
.TextDecorations = TextDecorations.Underline
End With
Dim TypeContentTB As New TextBlock
With TypeContentTB
.Text = "A short description of the type of work (e.g. Spinklers, Lights not working...)" & Environment.NewLine
.TextWrapping = TextWrapping.Wrap
End With
Dim DetailsHeaderTB As New TextBlock
With DetailsHeaderTB
.Text = "Details/Instructions"
.TextDecorations = TextDecorations.Underline
End With
Dim DetailsContentTB As New TextBlock
With DetailsContentTB
.Text = "Add any more details or instructions to help the supplier." & Environment.NewLine
.TextWrapping = TextWrapping.Wrap
End With
Dim SupplierHeaderTB As New TextBlock
With SupplierHeaderTB
.Text = "Supplier"
.TextDecorations = TextDecorations.Underline
End With
Dim SupplierContentTB As New TextBlock
With SupplierContentTB
.Text = "Insert the supplier using the button." & Environment.NewLine & Environment.NewLine & "You can still save the Work Order without entering a supplier, but the Work Order document will not be generated!" & Environment.NewLine
.TextWrapping = TextWrapping.Wrap
End With
Dim EmailHeaderTB As New TextBlock
With EmailHeaderTB
.Text = "Supplier Email"
.TextDecorations = TextDecorations.Underline
End With
Dim EmailContentTB As New TextBlock
With EmailContentTB
.Text = "The default email address will be loaded when you insert the supplier (this can be overridden) " & Environment.NewLine & Environment.NewLine & "If the email address is blank, or not valid, the Work Order document will not be emailed, but generated as a PDF to print locally and mail!" & Environment.NewLine
.TextWrapping = TextWrapping.Wrap
End With
Dim ImageHeaderTB As New TextBlock
With ImageHeaderTB
.Text = "Upload Image"
.TextDecorations = TextDecorations.Underline
End With
Dim ImageContentTB As New TextBlock
With ImageContentTB
.Text = "If you have a photograph of the work required, browse to the image. It will be included in the Work Order document." & Environment.NewLine
.TextWrapping = TextWrapping.Wrap
End With
Dim CostHeaderTB As New TextBlock
With CostHeaderTB
.Text = "Estimated Cost"
.TextDecorations = TextDecorations.Underline
End With
Dim vCost As String = "If you enter an estimated cost the Work Order will be authorised up to that amount" & Environment.NewLine & Environment.NewLine
vCost += "If the supplier is unable to carry out the work for this amount or less (or the field is left at zero) the work will not be authorised and they must revert back to the sender with a quote!" & Environment.NewLine & Environment.NewLine
vCost += "When estimated costs are used a lot it is a good idea to check the supplier invoices do not regularly equal (or a dollar or two less) than the estimated amount!!" & Environment.NewLine
Dim CostContentTB As New TextBlock
With CostContentTB
.Text = vCost
.TextWrapping = TextWrapping.Wrap
End With
Dim SiteHeaderTB As New TextBlock
With SiteHeaderTB
.Text = "Smart Manager"
.TextDecorations = TextDecorations.Underline
.Foreground = New SolidColorBrush(Colors.Blue)
End With
Dim SiteContentTB As New TextBlock
With SiteContentTB
.Text = "You can also enter Work Orders, whilst on-site, with a Smart Phone or Tablet using Smart Manager!" & Environment.NewLine
.TextWrapping = TextWrapping.Wrap
.Foreground = New SolidColorBrush(Colors.Blue)
End With
With SP.Children
.Add(HeaderTB)
.Add(DatesHeaderTB)
.Add(DatesContentTB)
.Add(UnitHeaderTB)
.Add(UnitContentTB)
.Add(TypeHeaderTB)
.Add(TypeContentTB)
.Add(DetailsHeaderTB)
.Add(DetailsContentTB)
.Add(SupplierHeaderTB)
.Add(SupplierContentTB)
.Add(EmailHeaderTB)
.Add(EmailContentTB)
.Add(ImageHeaderTB)
.Add(ImageContentTB)
.Add(CostHeaderTB)
.Add(CostContentTB)
.Add(SiteHeaderTB)
.Add(SiteContentTB)
End With
LogLB.Content = SP
End If
Return MainGrid
Catch ex As Exception
EmailError(ex)
Return Nothing
End Try
End Function
..and that is part of the main grid...
Private Function CentreGrid() As Grid
Try
Dim vGrid As New Grid
For i As Integer = 0 To 2
Dim vCol As New ColumnDefinition
If i = 1 Then
vCol.Width = New GridLength(5, GridUnitType.Auto)
End If
vGrid.ColumnDefinitions.Add(vCol)
Next
Dim vLeftGrid As Grid = LeftGrid()
Grid.SetColumn(vLeftGrid, 0)
vGrid.Children.Add(vLeftGrid)
Dim vGridSplitter As New GridSplitter
With vGridSplitter
.VerticalAlignment = Windows.VerticalAlignment.Stretch
.HorizontalAlignment = Windows.HorizontalAlignment.Center
.ResizeBehavior = GridResizeBehavior.PreviousAndNext
.Background = New SolidColorBrush(Colors.Blue)
.Width = 5
.Margin = New Thickness(5)
End With
Grid.SetColumn(vGridSplitter, 1)
vGrid.Children.Add(vGridSplitter)
Dim vRightGrid As Grid = RightGrid()
Grid.SetColumn(vRightGrid, 2)
vGrid.Children.Add(vRightGrid)
Return vGrid
Catch ex As Exception
EmailError(ex)
Return Nothing
End Try
End Function
When the page is opened this is what the horizontal line looks like (the one in blue)
..and this is what happens when the GridSplitter is moved to the left (same thing if the window is made larger)

Best would be to set a Binding for the X2 property and set it to ActualWidth of the TextBlock.
Below is C# code, you can change it to VB.net :
Line line = new Line() { Stroke = Brushes.Blue};
Binding binding = new Binding("ActualWidth");
binding.RelativeSource = new RelativeSource() { Mode=RelativeSourceMode.FindAncestor, AncestorType=typeof(TextBlock) };
BindingOperations.SetBinding(line, Line.X2Property, binding);
LogTB.Inlines.Add(line);

I would not use code-behind for something like this. Absolutely painful. You'll find WPF much easier to work with if you confine the majority of UI development to an XAML front-end. Try something like this
<Window x:Class="WpfApplication3.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:WpfApplication3"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.Resources>
<DataTemplate x:Key="MyTemplate">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding}" />
<Line X1="0" X2="{Binding ActualWidth, RelativeSource={RelativeSource Self}}" Stroke="Black" />
</StackPanel>
</DataTemplate>
</Grid.Resources>
<ItemsControl ItemTemplate="{StaticResource MyTemplate}">
<ItemsControl.Items>
<sys:String>One</sys:String>
<sys:String>Two</sys:String>
<sys:String>Three</sys:String>
</ItemsControl.Items>
</ItemsControl>
</Grid>
</Window>

Related

WPF DataGrid when compressed shuffles the values

The problem
WPF Datagrid. A dataset with one table and three columns. Price, Discount, Total. The only editable column is the discount. If I enter the data with the grid fully visible, everything works as it should.
But if I narrow the window and enter the data, they get mixed up. And it just messes up the values that I can't see at the end of editing the current cell. Seems to do it out of spite!
The result when I view the whole grid by enlarging the window is this:
As you can see the totals are mixed!
The code
I've recreated a minimal example that you can test yourself.
XAML
<Window x:Class="DataGrid"
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:TestDatagrid"
mc:Ignorable="d"
Title="DataGrid" Height="450" Width="800" Loaded="Window_Loaded">
<Grid>
<DataGrid x:Name="grid" Margin="0,28,0,38"
CanUserDeleteRows="True"
CanUserSortColumns="True"
CanUserAddRows="False"
SelectionUnit="CellOrRowHeader"
CellEditEnding="grid_CellEditEnding"/>
</Grid>
</Window>
VB.NET
Imports System.Data
Imports System.Globalization
Public Class DataGrid
Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs)
Dim ds As New DataSet
Dim tb As New DataTable("Orders")
Dim price As New DataColumn
price.ColumnName = "Price"
price.DataType = System.Type.GetType("System.Decimal")
tb.Columns.Add(price)
Dim discount As New DataColumn
discount.ColumnName = "Discount"
discount.DataType = System.Type.GetType("System.Decimal")
tb.Columns.Add(discount)
Dim total As New DataColumn
total.ColumnName = "Total"
total.DataType = System.Type.GetType("System.Decimal")
tb.Columns.Add(total)
For i As Integer = 0 To 20
Dim dr As DataRow = tb.NewRow()
dr("Price") = CDec((i + 1) * 10)
dr("Discount") = 0
dr("Total") = CDec((i + 1) * 10)
tb.Rows.Add(dr)
Next
ds.Tables.Add(tb)
grid.ItemsSource = ds.Tables(0).DefaultView
For Each c In grid.Columns
If c.Header = "Price" Then c.IsReadOnly = True
If c.Header = "Total" Then c.IsReadOnly = True
Next
End Sub
Private Sub grid_CellEditEnding(sender As Object, e As DataGridCellEditEndingEventArgs)
If e.EditAction = DataGridEditAction.Commit Then
Dim nfi As NumberFormatInfo = New NumberFormatInfo()
nfi.NumberDecimalSeparator = "."
Try
Dim tPrice As TextBlock = TryCast(grid.Columns(0).GetCellContent(e.Row), TextBlock)
Dim Price As Double = CDbl("0" & Replace(tPrice.Text, ".", ","))
Dim Discount As Double = CDbl("0" & Replace(TryCast(e.EditingElement, TextBox).Text, ".", ","))
Dim Total As Double = Price * (1 - (Discount / 100))
' Format the discount value
e.EditingElement.SetValue(TextBox.TextProperty, String.Format(nfi, "{0:0.00#}", Discount))
' Format and show the value on datagrid
TryCast(grid.Columns(2).GetCellContent(e.Row), TextBlock).Text = String.Format(nfi, "{0:0.00#}", Total)
' Update the value on grid
TryCast(grid.CurrentItem, DataRowView).Item(2) = Total
Catch ex As Exception
End Try
End If
End Sub
End Class
I really have no idea what's going on!
Thanks for any hints or help you will give me.
I really have no idea what's going on!
Virtualization, i.e. the elements that you wrongfully edit are resued for for performance reasons.
The obvious and recommended solution is to avoid editing the visual elemens directly and instead edit the columns of the underlying DataRowView.
The other solution is to disable the virtualization:
<DataGrid VirtualizingPanel.IsVirtualizing="False" EnableRowVirtualization="False" ... />

RadioButton does not show up when inside a panel within a panel

This is my first question ever, so please spare me if I did something wrong.
I have a small survey form that automatically draw questions and answers from a SQL server table, and create a question label (Label_Questionnaire(i)), a panel to nest all radiobuttons for the answer of each question (Panel_Response(i)), and 3 radiobuttons(yes, no, n/a), named RadioButton_Answers(i)_1 . All questions and answers are inside a big panel (Panel_Survey) to allow user to scroll up and down (around 50 questions).
When I run the program, I can only see questions but none of the radiobuttons are showing. What I did try are:
Use .BringToFront to bring the Panel_Response and all radiobuttons to front.
Change .Parent to Controls.Add . Instead of using .Parent, I use Panel_Survey.Controls.Add(Panel_Response) and Panel_Response.Controls.Add(RadioButton_Answers_1)
Force Panel_Response.Visible = True and all radiobuttons visible = true
(I know it might sound stupid, but I'm out of trick)
How do I make those radiobuttons show up? If not, are there any better designs for this kind of survey form? Thank you for any advice, in advance!
Below is my code:
Protected Overrides Sub OnLoad(e As EventArgs)
Dim PanelCount As Integer
Dim QuestionName As String
Dim Response1 As String
Dim Response2 As String
Dim Response3 As String
Dim InitialX As Integer = Panel_Survey.Left
Dim InitialY As Integer = Panel_Survey.Top
Dim SizeX As Integer = 1000
Dim SizeY As Integer = 25
'Load the survey
Try
'Get a list of questions and answers into array of list
Dim ListofQuestionandAnswers As New List(Of List(Of String))
Dim conn As New SqlClient.SqlConnection
conn.ConnectionString = ConnectionString
Dim CommandString As String = "SELECT [QuestionID], [QuestionName] ,[Response1],[Response2],[Response3] FROM [Question_List] ORDER BY [QuestionID]"
Dim Command As New SqlClient.SqlCommand
Command.CommandText = CommandString
Command.Connection = conn
Dim dr As SqlClient.SqlDataReader
conn.Open()
dr = Command.ExecuteReader
While dr.Read
Dim ls As New List(Of String)
ls.Add(dr.GetValue(0).ToString)
ls.Add(dr.GetValue(1).ToString)
ls.Add(dr.GetValue(2).ToString)
ls.Add(dr.GetValue(3).ToString)
ls.Add(dr.GetValue(4).ToString)
ListofQuestionandAnswers.Add(ls)
End While
conn.Close()
PanelCount = ListofQuestionandAnswers.Count
For i = 0 To ListofQuestionandAnswers.Count - 1
QuestionName = ListofQuestionandAnswers(i)(1)
Response1 = ListofQuestionandAnswers(i)(2)
Response2 = ListofQuestionandAnswers(i)(3)
Response3 = ListofQuestionandAnswers(i)(4)
Dim Label_Questionnaire As New Label
Dim Panel_Response As New Panel
Dim RadioButton_Answers_1 As New RadioButton
Dim RadioButton_Answers_2 As New RadioButton
Dim RadioButton_Answers_3 As New RadioButton
'Condition the label
With Label_Questionnaire
.Parent = Panel_Survey
.Name = "Label_Questionnaire" + i.ToString
.Font = New Font("Calibri", 11, FontStyle.Regular)
.Text = QuestionName
.ForeColor = Color.Black
.Location = New Point(InitialX, InitialY)
.AutoSize = True
End With
'Condition the panel
With Panel_Response
'Panel_Survey.Controls.Add(Panel_Response)
.Parent = Panel_Survey
.Name = "Panel_Questionnaire" + i.ToString
.Location = New Point(InitialX + 880, InitialY)
.Width = 250
.Height = 25
.BringToFront()
End With
Dim j As Integer
Dim h As Integer
j = Panel_Response.Left
h = Panel_Response.Top
'Condition the radiobuttons for answers
With RadioButton_Answers_1
.Parent = Panel_Response
.Name = "RadioButton_Answers" + i.ToString + "_1"
.Font = New Font("Calibri", 11, FontStyle.Regular)
.Text = Response1
.ForeColor = Color.Black
.Location = New Point(j, h)
.AutoSize = True
h += RadioButton_Answers_1.Height
End With
With RadioButton_Answers_2
.Parent = Panel_Response
.Name = "RadioButton_Answers" + i.ToString + "_2"
.Font = New Font("Calibri", 11, FontStyle.Regular)
.Text = Response2
.ForeColor = Color.Black
.Location = New Point(RadioButton_Answers_1.Right, h)
.AutoSize = True
End With
With RadioButton_Answers_3
.Parent = Panel_Response
.Name = "RadioButton_Answers" + i.ToString + "_3"
.Font = New Font("Calibri", 11, FontStyle.Regular)
.Text = Response3
.ForeColor = Color.Black
.Location = New Point(RadioButton_Answers_2.Right, h)
.AutoSize = True
End With
InitialY = InitialY + SizeY + 10
Next
Catch ex As Exception
MessageBox.Show(String.Format("Error: {0}", ex.Message), "Error while creating questions and answers", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
Few basic problems here:
You seem to assume that when a control is placed inside a panel, it must be placed at a location relative to the form. It doesn't; it places relative to the Panel which has its own coordinate system starting at 0,0 in the top left of the panel - you initialize j and h (for locations of the radio buttons) to the Left and Top of the panel they're in, but they should be inited to 0,0 if you want the radiobuttons to start at the top left of the panel. If the Panel is placed at 300,300 on a form, and you place a radiobutton inside the panel also at 300,300 (because you copied its left and top) then the radio button will look like it's at 600,600 on the form because its at 300,300 inside a panel that is at 300,300
You only make your panel 250 wide - it's barely wide enough to show a single radio button
You increment h once, by an amount that means the next radiobutton disappears off the bottom of the panel (which is 25 pixels high)
Here:
With Panel_Response
'Panel_Survey.Controls.Add(Panel_Response)
.Parent = Panel_Survey
.Name = "Panel_Questionnaire" + i.ToString
.Location = New Point(InitialX + 880, InitialY)
.Width = 2500
.Height = 25
.BringToFront()
.BackColor = Color.Red
End With
Dim j As Integer
Dim h As Integer
j = 0
h = 0
Paste that over your app code and run it again. I made the panel BackColor red so you can more easily see where the panel is
I guess you need to decide how you want your UI to look. If the radiobuttons are laid out vertically, don't increment X when you add them to the panel (by setting the parent property). Make the panel tall enough to accommodate them (25 px not enough)
Use a FlowLayoutPaanel or TableLayoutPanel instead

WPF create Retangles using Vb.net

I am trying to create a WPF windows application where it has to show all the Racks or shelves of Warehouse like below
So far this is what I have tried
My Xaml looks like
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="950" Width="1225" BorderBrush="DodgerBlue" BorderThickness="3">
<Grid>
<Canvas Height="900" Width="1200" Name="front_canvas" Grid.Row="0" Grid.Column="0" Margin="1,24,10,790" >
<Canvas.RenderTransform>
<ScaleTransform ScaleX="1" ScaleY="1" />
</Canvas.RenderTransform>
</Canvas>
</Grid>
</Window>
In a Method I have following
For i As Integer = 1 To front_canvas.Width - 1 Step 100
Dim rect As New Rectangle()
rect.StrokeThickness = 1
rect.Stroke = System.Windows.Media.Brushes.Black
rect.Width = 50
rect.Height = 50
rect.Name = "box" + i.ToString()
'If Not i / 2 = 0 Then
Canvas.SetLeft(rect, i)
Canvas.SetFlowDirection(rect, Windows.FlowDirection.LeftToRight)
'Canvas.SetTop(rect, Top)
'_top += rect.Height
If front_canvas.Children.Count > 0 Then
Dim lastChildIndex = front_canvas.Children.Count - 1
Dim lastChild = TryCast(front_canvas.Children(lastChildIndex), FrameworkElement)
If lastChild IsNot Nothing Then
_top = Canvas.GetTop(lastChild) + lastChild.Height + 1
End If
End If
Canvas.SetTop(rect, _top)
front_canvas.Children.Add(rect)
' End If
_rectangles.Add(rect)
Next
And the result I get here is like below
I would appreciate if someone can help me
Here try this:
XAML
<Window x:Class="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:WpfApplication7"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Canvas Name="cvsWarehouse"></Canvas>
</ScrollViewer>
Code Behind:
Imports System.Text
Class MainWindow
Private Const dRACKSIZE As Double = 57
Private Const dRACKSPACING As Double = 2
Private Const dAISLESPACING As Double = 40
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
CreateAisles()
End Sub
Private Sub CreateAisles()
cvsWarehouse.Width = 10 + (11 * dRACKSIZE) + (6 * dRACKSPACING) + (4 * dAISLESPACING)
cvsWarehouse.Height = 10 + (19 * dRACKSIZE) + (18 * dRACKSPACING)
Dim dStartX As Double = 10
CreateAisle(dStartX, 10, 19, 2, 1)
dStartX += dAISLESPACING
CreateAisle(dStartX, 10, 19, 2, 2)
dStartX += dAISLESPACING
CreateAisle(dStartX, 10, 19, 2, 3)
dStartX += dAISLESPACING
CreateAisle(dStartX, 10, 19, 2, 4)
dStartX += dAISLESPACING
CreateAisle(dStartX, 10, 19, 3, 5)
End Sub
Private Sub CreateAisle(ByRef dStartX As Double, dStarty As Double, iRowCount As Integer, iColCount As Integer, iAisleNumber As Integer)
Dim iColUpper = iColCount - 1
Dim iRowUpper = iRowCount - 1
For iCol As Integer = 0 To iColUpper
Dim dYOffset As Double = dStarty
For iRow As Integer = 0 To iRowUpper
Dim bdr As Border = GetNewBorder()
bdr.Child = GetNewTextBlock(iAisleNumber, iCol + 1, iRow + 1)
Canvas.SetTop(bdr, dYOffset)
Canvas.SetLeft(bdr, dStartX)
cvsWarehouse.Children.Add(bdr)
dYOffset += dRACKSIZE + dRACKSPACING
Next
dStartX += dRACKSIZE + dRACKSPACING
Next
dStartX -= dRACKSPACING
End Sub
Private Function GetNewBorder() As Border
Dim bdr As New Border
bdr.Width = dRACKSIZE
bdr.Height = dRACKSIZE
bdr.BorderBrush = Brushes.Red
bdr.BorderThickness = New Thickness(1)
bdr.CornerRadius = New CornerRadius(2)
Return bdr
End Function
Private Function GetNewTextBlock(iAisle As Integer, iCol As Integer, iRow As Integer) As TextBlock
Dim txtBlock As New TextBlock()
txtBlock.HorizontalAlignment = HorizontalAlignment.Center
txtBlock.VerticalAlignment = VerticalAlignment.Center
Dim sb As New StringBuilder()
sb.Append("A").Append(iAisle.ToString()).Append(":C").Append(iCol.ToString()).Append(":R").Append(iRow.ToString())
txtBlock.Text = sb.ToString()
Return txtBlock
End Function
End Class
if you want to use buttons instead of borders that have event handlers attached:
Private Sub CreateAisle(ByRef dStartX As Double, dStarty As Double, iRowCount As Integer, iColCount As Integer, iAisleNumber As Integer)
Dim iColUpper = iColCount - 1
Dim iRowUpper = iRowCount - 1
For iCol As Integer = 0 To iColUpper
Dim dYOffset As Double = dStarty
For iRow As Integer = 0 To iRowUpper
Dim btn As Button = GetNewButton()
btn.Content = GetNewTextBlock(iAisleNumber, iCol + 1, iRow + 1)
Canvas.SetTop(btn, dYOffset)
Canvas.SetLeft(btn, dStartX)
cvsWarehouse.Children.Add(btn)
dYOffset += dRACKSIZE + dRACKSPACING
Next
dStartX += dRACKSIZE + dRACKSPACING
Next
dStartX -= dRACKSPACING
End Sub
Private Function GetNewButton() As Button
Dim btn As New Button
btn.Width = dRACKSIZE
btn.Height = dRACKSIZE
btn.BorderBrush = Brushes.Red
btn.BorderThickness = New Thickness(1)
btn.AddHandler(Button.ClickEvent, New RoutedEventHandler(AddressOf Button_Click))
Return btn
End Function
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
Dim buttonClicked As Button = DirectCast(sender, Button)
Dim text As String = DirectCast(buttonClicked.Content, TextBlock).Text
MessageBox.Show("Button " & text & " Clicked")
End Sub
To the questions below in answer 1, Here is what I did. Most of the code is from SO but it is not actually doing the styles like width and height. Print Preview is done in the following way. I had a Print preview button in tool bar of XAML mainwindow and in code behind I had below code `
Friend Sub DoPreview(title As String)
Dim fileName As String = System.IO.Path.GetRandomFileName()
' Dim visual As FlowDocumentScrollViewer = DirectCast(Me.FindName("cvsWarehouse"), FlowDocumentScrollViewer)
Dim visual As Canvas = DirectCast(cvsWarehouse, Canvas)
visual.Width = 1500
visual.Height = 900
Try
' write the XPS document
Using doc As New XpsDocument(fileName, FileAccess.ReadWrite)
Dim writer As XpsDocumentWriter = XpsDocument.CreateXpsDocumentWriter(doc)
writer.Write(visual)
End Using
' Read the XPS document into a dynamically generated
' preview Window
Using doc As New XpsDocument(fileName, FileAccess.Read)
Dim fds As FixedDocumentSequence = doc.GetFixedDocumentSequence()
Dim s As String = _previewWindowXaml
s = s.Replace("##TITLE", title.Replace("'", "&apos;"))
Using reader = New System.Xml.XmlTextReader(New StringReader(s))
Dim preview As New Window
preview.Width = 1500
preview.Height = 900
preview.HorizontalAlignment = Windows.HorizontalAlignment.Stretch
preview.VerticalAlignment = Windows.VerticalAlignment.Stretch
preview = TryCast(System.Windows.Markup.XamlReader.Load(reader), Window)
Dim dv1 As DocumentViewer = TryCast(LogicalTreeHelper.FindLogicalNode(preview, "dv1"), DocumentViewer)
dv1.Width = 1500
dv1.Height = 900
dv1.IsHitTestVisible = True
dv1.VerticalAlignment = Windows.VerticalAlignment.Stretch
dv1.HorizontalAlignment = Windows.HorizontalAlignment.Stretch
dv1.ApplyTemplate()
dv1.Document = TryCast(fds, IDocumentPaginatorSource)
preview.ShowDialog()
End Using
End Using
Finally
If File.Exists(fileName) Then
Try
File.Delete(fileName)
Catch
End Try
End If
End Try
End Sub`
And the my preview is like below
Print Preview Image
Neither I can do scroll bar nor the entire canvas which is wider is fit to the preview window.
My actual Window with canvas is way wider like below
Actual canvas window
Could someone please correct the bug here?

WFP - odd behaviour when opening new tab

The user can select from a DataGrid either by double clicking on the row, or selecting the row and clicking a button.
Using the first method the new page is initialised but the loaded event is not fired.
Using the second method the new page is initialised, the old one fires the unloaded event and the new one fires the loaded event and the new tab opens.
As both the click and doubleclick events are firing the same sub I can't figure out why one works and the other doesn't - when not in debug the new tab is formed using the first method and when clicked the loaded event is then fired, but this doesn't show in debug.
Private Sub Reports_BalanceSheets_EditRecord(sender As Object, e As RoutedEventArgs)
Try
NewRecord = False
Dim DGV As CustomControl.DGVx = Reports_BalanceSheets_Grid.FindName("Reports_BalanceSheets_DGV")
If DGV.SelectedItems.Count = 1 Then
Dim row As System.Data.DataRowView = DGV.SelectedItems(0)
FormID = row("ID")
Dim vName As String = row("Name")
Dim vTab As CustomControl.STC_Tabx = Application.Current.MainWindow.FindName("Reports_BalanceSheetTab")
Dim TabControl As CustomControl.STCx = Application.Current.MainWindow.FindName("AccountingReports_TabControl")
Dim vImageSource As String = ReturnImageAsString("Profit_Loss.png", 16)
If vTab Is Nothing Then
Dim ReportsBalanceSheetFrame As New Frame
Dim Tab As New CustomControl.STC_Tabx
With Tab
.Name = "Reports_BalanceSheetTab"
.Header = " Edit " & vName & " "
.CloseButtonVisibility = DevComponents.WpfEditors.eTabCloseButtonVisibility.Visible
.TabToolTip = "Edit " & vName
.ImageSource = vImageSource
.Content = ReportsBalanceSheetFrame
End With
AddHandler Tab.Closing, AddressOf TabControl_TabClosing
Dim vGrid As Grid = Application.Current.MainWindow.FindName("MainGrid_Website")
RegisterControl(vGrid, Tab, Tab.Name.ToString)
TabControl.Items.Add(Tab)
Dim BalanceSheet As New Reports_BalanceSheet_Page
ReportsBalanceSheetFrame.NavigationService.Navigate(BalanceSheet)
TabControl.SelectedItem = Tab
Else
vTab.Close()
Dim ReportsBalanceSheetFrame As New Frame
Dim Tab As New CustomControl.STC_Tabx
With Tab
.Name = "Reports_BalanceSheetTab"
.Header = " Edit " & vName & " "
.CloseButtonVisibility = DevComponents.WpfEditors.eTabCloseButtonVisibility.Visible
.TabToolTip = "Edit " & vName
.ImageSource = vImageSource
.Content = ReportsBalanceSheetFrame
End With
AddHandler Tab.Closing, AddressOf TabControl_TabClosing
Dim vGrid As Grid = Application.Current.MainWindow.FindName("MainGrid_Website")
RegisterControl(vGrid, Tab, Tab.Name.ToString)
TabControl.Items.Add(Tab)
Dim BalanceSheet As New Reports_BalanceSheet_Page
ReportsBalanceSheetFrame.NavigationService.Navigate(BalanceSheet)
TabControl.SelectedItem = Tab
End If
ElseIf DGV.SelectedItems.Count > 1 Then
AppBoxValidation("You can only select one item at a time to edit!")
Else
AppBoxValidation("You must select an item to edit!")
End If
Catch ex As Exception
EmailError(ex)
End Try
End Sub
Adding e.Handled resolved the issue

Alignment on dynamically created items

Hi,
Based on the image above, I would like to align the words "Action", "Adventure", "Arcade" to left most and align it to appear same height as the blue right arrow
my code for this is created dynamically:
Dim intRow As Integer
Dim intColumn As Integer
intRow = 0
intColumn = 0
Dim rd As RowDefinition
For Each abc In ArrayOfItems
Dim newButton As New Button
newButton.MaxHeight = 35
newButton.MaxWidth = 300
newButton.Background = Brushes.Transparent
newButton.BorderBrush = Brushes.Transparent
newButton.Tag = abc.application_category_id
Dim sp As New StackPanel
sp.Orientation = Orientation.Horizontal
sp.HorizontalAlignment = Windows.HorizontalAlignment.Left
sp.VerticalAlignment = Windows.VerticalAlignment.Center
Dim Image2 As New Image
Dim src As New Uri("/Images/chevron_blue.png", UriKind.Relative)
Dim img As New BitmapImage(src)
Image2.Width = 10
Image2.Height = 10
Image2.HorizontalAlignment = Windows.HorizontalAlignment.Left
Image2.VerticalAlignment = Windows.HorizontalAlignment.Left
Image2.Source = img
Dim LabelApp As New Label
LabelApp.Content = abc.category_name
LabelApp.FontWeight = FontWeights.Bold
LabelApp.FontSize = 9
LabelApp.MaxWidth = 270
LabelApp.Foreground = Brushes.Black
LabelApp.HorizontalContentAlignment = Windows.HorizontalAlignment.Left
LabelApp.VerticalAlignment = Windows.HorizontalAlignment.Left
sp.Children.Add(LabelApp)
sp.Children.Add(Image2)
newButton.Content = sp
Grid.SetColumn(newButton, intColumn)
Grid.SetRow(newButton, intRow)
gridCategoryGames.Children.Add(newButton)
intRow = intRow + 1
rd = New RowDefinition With {.Height = GridLength.Auto}
gridCategoryGames.RowDefinitions.Add(rd)
gridCategoryGames.UpdateLayout()
Next abc
End If
Ouch, that is an ugly way to do it :)
Fortunately WPF has something that will cut your code down substantially, the ItemsControl. As you already have ArrayOfItems which is obviously IEnumerable, you can craft you XAML like this:
<ItemsControl ItemsSource="{Binding ArrayOfItems}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate >
<StackPanel Orientation="Vertical" HorizontalAlignment="Right" >
<TextBlock Text="myText"/>
<Image Source="path to my image" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
You can then get rid of all that code that is manually creating items. The controls specified in ItemsControl.ItemTemplate will be repeated for every item in your list. The sky is the limit with this approach (there are literally thousands of examples of how to use this control).

Resources