Nice and easy one to explain, but struggling to find an answer.
In the below code, a nice pretty box opens up, and when you select a row, a sub row appears with further data.
What I'm trying to do is get access to the sub row headers.
[void][System.Reflection.Assembly]::LoadWithPartialName( 'presentationframework' )
[xml]$XAML = #'
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Title="Global Core Desktop Toolbox" Height="550" Width="800" WindowStartupLocation="CenterScreen"
WindowStyle="ToolWindow">
<Grid>
<Grid Background="#FF0A2F64">
<DataGrid Name="Datagrid" AutoGenerateColumns="True" HorizontalAlignment="Left" VerticalAlignment="Top"
Height="250" Width="675" Margin="40,125,0,0" Visibility="Visible" IsReadOnly="True" >
<DataGrid.Columns>
<DataGridTextColumn Header="Domain" Binding="{Binding Domain}" Width="Auto" />
<DataGridTextColumn Header="Total Live PCs" Binding="{Binding LiveMachines}" Width="Auto"/>
<DataGridTextColumn Header="DR" Binding="{Binding DR}" Width="40"/>
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<DockPanel Background="GhostWhite">
<Grid Margin="0,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="Domain: " FontWeight="Bold" Grid.Row="1" />
<TextBlock Text="{Binding Domain}" Grid.Column="1" Grid.Row="1" />
<TextBlock Text="Disabled Machines: " FontWeight="Bold" Grid.Row="2" />
<TextBlock Text="{Binding SamAccountDisabled}" Grid.Column="1" Grid.Row="2" />
<TextBlock Text="Servers: " FontWeight="Bold" Grid.Row="3" />
<TextBlock Text="{Binding Servers}" Grid.Column="1" Grid.Row="3" />
</Grid>
</DockPanel>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
</Grid>
</Grid>
</Window>
'#
#Read XAML
$READER = ( New-Object System.Xml.XmlNodeReader $XAML )
Try {
$FORM = [Windows.Markup.XamlReader]::Load( $READER )
}
Catch {
Write-Host "Unable to load Windows.Markup.XamlReader."
Sleep 4
}
$XAML.SelectNodes( "//*[#Name]" ) | %{ Set-Variable -Name ( $_.Name ) -Value
$FORM.FindName( $_.Name ) }
$datagrid.AddChild( [pscustomobject]#{
Domain = "happytimes.local"
LiveMachines = 6
DR = 1
Servers = 2
SamAccountDisabled = 3
} )
$form.ShowDialog()
So if i do $datagrid.Columns.header , I have access to "Domain", "Total Live PCs" and "DR" - which i use in a foreach loop. It's the "Domain", "Disabled Machines" and "Servers" I'm trying to get to. $datagrid.items gives me everything with the numbers, but I can't break it down further.
Am i missing something? Any ideas how I get to the variable?
Thanks
Related
I am trying to retrieve the text from the ComboBox. Searching for the answer, it seems that I need to use the Add_SelectedValueChanged method, but I do not see that method available on my $form variable. I also ckecked the $WPFproductTxt variable, but it isn't there either. What am I missing?
My code snippet (ignore the whitespace in the form, I took fields out for testing):
$rawXAML = #"
<Window x:Class="WpfApp8.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:WpfApp8"
mc:Ignorable="d"
Title="Test" Height="650" Width="425">
<Grid Margin="0,0,0,1">
<TextBlock x:Name="textBlock1" HorizontalAlignment="Left" Margin="10,10,0,0" Text="Test form" TextWrapping="Wrap" VerticalAlignment="Top" FontSize="16" FontWeight="Bold"/>
<Label x:Name="custNameLabel" Content="Customer Name*" Margin="10,43,262,0" VerticalAlignment="Top" />
<TextBox x:Name="custNameTxt" Height="26" Margin="158,43,10,0" TextWrapping="Wrap" VerticalAlignment="Top" Text="" />
<StackPanel Margin="10,263,10,269">
<Label FontWeight="DemiBold" BorderThickness="2" BorderBrush="Gray" HorizontalContentAlignment="Center">Hardware</Label>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<RadioButton x:Name="sharedMark" Content="Shared" IsChecked="False" HorizontalAlignment="Center"/>
<RadioButton x:Name="dedicatedMark" Content="Dedicated" IsChecked="False" Grid.Row="1" HorizontalAlignment="Center"/>
<ComboBox x:Name="productTxt" Margin="0,5,0,5" Grid.Row="0" Grid.Column="1" Grid.RowSpan="2" ToolTip="Hardware type." HorizontalAlignment="Center" Width="182">
<ComboBoxItem Content="Gen 3 SFF" IsSelected="False"/>
<ComboBoxItem Content="Gen 3 Rack" IsSelected="False"/>
<ComboBoxItem Content="Gen 4 Rack" IsSelected="False"/>
<ComboBoxItem Content="Virtual Machine" IsSelected="False"/>
</ComboBox>
</Grid>
</StackPanel>
<Button x:Name="okButton" Content="OK" HorizontalAlignment="Left" Margin="13,578,0,0" VerticalAlignment="Top" Width="50" />
<Button x:Name="cancelButton" Content="Cancel" HorizontalAlignment="Left" Margin="68,578,0,0" VerticalAlignment="Top" Width="50"/>
</Grid>
</Window>
"#
$rawXAML = $rawXAML -replace 'mc:Ignorable="d"', '' -replace "x:N", 'N' -replace '^<Win.*', '<Window'
[void][System.Reflection.Assembly]::LoadWithPartialName('PresentationFramework')
[xml]$XAML = $rawXAML
# Read XAML.
$reader = (New-Object System.Xml.XmlNodeReader $xaml)
$form = [Windows.Markup.XamlReader]::Load($reader)
$xaml.SelectNodes("//*[#Name]") | ForEach-Object { Set-Variable -Name "WPF$($_.Name)" -Value $Form.FindName($_.Name) }
$WPFokButton.Add_Click( {
$form.Close()
})
$WPFcancelButton.Add_Click( { Set-Variable -Name UserCancelled -Value $true -Scope Global })
$null = $form.ShowDialog()
I didn't knew either so I entered this into the console to get the events available to the ComboBox:
$WPFproductTxt | Get-Member -MemberType Event
This lists a SelectionChanged event. Thus I was able to write:
$WPFproductTxt.Add_SelectionChanged({
[Windows.MessageBox]::Show( $WPFproductTxt.SelectedValue )
})
This event shows a message box when I select a different ComboBox item.
I have a script with a gui based on native WPF and XAML (from here: https://foxdeploy.com/functions/ise-snippets/xaml-to-gui/ ).
The hierarchical structure XAMl Code is from this tutorial: https://dlaa.me/blog/post/9898803 .
For now I try to figure out how should a pscustomobject should look like to pass it to "Binding SubItems".
How can I declare what is a parent item and whats a childitem?
I think its very easy but I can not get it. I have read and tried a lot but can't find any native WPF examples.
Could anyone point me into the right direction. I appreciate any help.
$inputXML = #"
<Window x:Class="SimpleTreeGridUX.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="SimpleTreeGridUX" Height="400" Width="400">
<Grid Margin="10">
<!-- TreeGrid "Control" -->
<Border BorderBrush="Black" BorderThickness="1">
<!-- Resources -->
<Border.Resources>
<Style x:Key="TextBlockStyle" TargetType="{x:Type TextBlock}">
<Setter Property="Margin" Value="3 0 3 0"/>
</Style>
<Style x:Key="TextBlockBoldStyle" TargetType="{x:Type TextBlock}" BasedOn="{StaticResource TextBlockStyle}">
<Setter Property="FontWeight" Value="Bold"/>
</Style>
</Border.Resources>
<!-- Content -->
<Grid Grid.IsSharedSizeScope="True">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<!-- Column headers -->
<TreeViewItem Grid.Row="0" BorderThickness="1">
<TreeViewItem.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Task"/>
<!-- Placeholders for two columns of ToggleButton -->
<ColumnDefinition SharedSizeGroup="Toggle"/>
<ColumnDefinition SharedSizeGroup="Toggle"/>
<ColumnDefinition SharedSizeGroup="Duration"/>
<ColumnDefinition SharedSizeGroup="Notes"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Task" Style="{StaticResource TextBlockBoldStyle}"/>
<!-- Empty TreeViewItem to measure the size of its ToggleButton into the "Toggle" group-->
<TreeViewItem Grid.Column="1" Padding="0"/>
<TextBlock Grid.Column="3" Text="Duration" Style="{StaticResource TextBlockBoldStyle}"/>
<TextBlock Grid.Column="4" Text="Notes" Style="{StaticResource TextBlockBoldStyle}"/>
</Grid>
</TreeViewItem.Header>
</TreeViewItem>
<!-- Data rows -->
<TreeView Grid.Row="1" x:Name="treeview" ItemsSource="{Binding SubItems}" BorderBrush="Gray" BorderThickness="0 1 0 0">
<TreeView.ItemTemplate>
<!-- Level 0 template leaves space for 2 child "Toggle" levels -->
<HierarchicalDataTemplate ItemsSource="{Binding SubItems}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Task"/>
<ColumnDefinition SharedSizeGroup="Toggle"/>
<ColumnDefinition SharedSizeGroup="Toggle"/>
<ColumnDefinition SharedSizeGroup="Duration"/>
<ColumnDefinition SharedSizeGroup="Notes"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Task}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="3" Text="{Binding Duration}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="4" Text="{Binding Notes}" Style="{StaticResource TextBlockStyle}"/>
</Grid>
<!-- Level 1 template leaves space for 1 child "Toggle" level -->
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding SubItems}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Task"/>
<ColumnDefinition/>
<ColumnDefinition SharedSizeGroup="Toggle"/>
<ColumnDefinition SharedSizeGroup="Duration"/>
<ColumnDefinition SharedSizeGroup="Notes"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Task}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="3" Text="{Binding Duration}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="4" Text="{Binding Notes}" Style="{StaticResource TextBlockStyle}"/>
</Grid>
<!-- Level 2 template has no children -->
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding SubItems}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Task"/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition SharedSizeGroup="Duration"/>
<ColumnDefinition SharedSizeGroup="Notes"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Task}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="3" Text="{Binding Duration}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="4" Text="{Binding Notes}" Style="{StaticResource TextBlockStyle}"/>
</Grid>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
</Border>
</Grid>
</Window>
"#
$inputXML = $inputXML -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^<Win.*', '<Window'
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
[xml]$XAML = $inputXML
#Read XAML
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
try{$Form=[Windows.Markup.XamlReader]::Load( $reader )}
catch [System.Management.Automation.MethodInvocationException] {
Write-Warning "We ran into a problem with the XAML code. Check the syntax for this control..."
write-host $error[0].Exception.Message -ForegroundColor Red
if ($error[0].Exception.Message -like "*button*"){
write-warning "Ensure your <button in the `$inputXML does NOT have a Click=ButtonClick property. PS can't handle this`n`n`n`n"}
}
catch{#if it broke some other way <img draggable="false" class="emoji" alt="π" src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f600.svg">
Write-Host "Unable to load Windows.Markup.XamlReader. Double-check syntax and ensure .net is installed."
}
#===========================================================================
# Store Form Objects In PowerShell
#===========================================================================
$xaml.SelectNodes("//*[#Name]") | %{Set-Variable -Name "WPF$($_.Name)" -Value $Form.FindName($_.Name)}
Function Get-FormVariables{
if ($global:ReadmeDisplay -ne $true){Write-host "If you need to reference this display again, run Get-FormVariables" -ForegroundColor Yellow;$global:ReadmeDisplay=$true}
write-host "Found the following interactable elements from our form" -ForegroundColor Cyan
get-variable WPF*
}
Get-FormVariables
#===========================================================================
# Use this space to add code to the various form elements in your GUI
#===========================================================================
$Data = #{
Task = "Main-Task";
Duration = "10";
Notes = "notes";
SubItems = #{
Task = "Task Ebene 2";
Duration = "20";
Notes = "notes2";
SubItems = #{
Task = "Task Ebene 3";
Duration = "30";
Notes = "notes3";
}
}
}
$Object = New-Object -TypeName PSObject -Property $Data
$WPFtreeview.items.Add($Object)
#Reference
#Adding items to a dropdown/combo box
#$vmpicklistView.items.Add([pscustomobject]#{'VMName'=($_).Name;Status=$_.Status;Other="Yes"})
#Setting the text of a text box to the current PC name
#$WPFtextBox.Text = $env:COMPUTERNAME
#Adding code to a button, so that when clicked, it pings a system
# $WPFbutton.Add_Click({ Test-connection -count 1 -ComputerName $WPFtextBox.Text
# })
#===========================================================================
# Shows the form
#===========================================================================
write-host "To show the form, run the following" -ForegroundColor Cyan
$Form.ShowDialog() | out-null
#snicz: Thank you very much for your answer, I don't know why your answer is disappeared, guess a technical problem from stackoverflow.
regarding your answer, you posted the code from David Anson's Project:
TreeViewItem ParentItem = new TreeViewItem();
ParentItem.Header = "Parent";
TreeView1.Items.Add(ParentItem);
//
TreeViewItem Child1Item = new TreeViewItem();
Child1Item.Header = "Child One";
ParentItem.Items.Add(Child1Item);
//
TreeViewItem Child2Item = new TreeViewItem();
Child2Item.Header = "Child Two";
ParentItem.Items.Add(Child2Item);
TreeViewItem SubChild1Item = new TreeViewItem();
SubChild1Item.Header = "Sub Child One";
Child2Item.Items.Add(SubChild1Item);
TreeViewItem SubChild2Item = new TreeViewItem();
SubChild2Item.Header = "Sub Child Two";
Child2Item.Items.Add(SubChild2Item);
TreeViewItem SubChild3Item = new TreeViewItem();
SubChild3Item.Header = "Sub Child Three";
Child2Item.Items.Add(SubChild3Item);
//
TreeViewItem Child3Item = new TreeViewItem();
Child3Item.Header = "Child Three";
ParentItem.Items.Add(Child3Item);
//
ParentItem.IsExpanded = true;
Child2Item.IsExpanded = true;
}
I also found this but I'm looking for a "powershell only" way, how would exactly this looks like if I create pscustomobject?
I also found some Windows Forms Examples but I would like to include it in a project thats only XAML and WPF/Powershell (like the Code in initial Question above).
I'm not sure if its clear what I'am talking about?
got it working this way:
Sure, there is a much shorter way, but it may help someone to understand it easier.
$inputXML = #"
<Window x:Class="SimpleTreeGridUX.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="SimpleTreeGridUX" Height="400" Width="400">
<Grid Margin="10">
<!-- TreeGrid "Control" -->
<Border BorderBrush="Black" BorderThickness="1">
<!-- Resources -->
<Border.Resources>
<Style x:Key="TextBlockStyle" TargetType="{x:Type TextBlock}">
<Setter Property="Margin" Value="3 0 3 0"/>
</Style>
<Style x:Key="TextBlockBoldStyle" TargetType="{x:Type TextBlock}" BasedOn="{StaticResource TextBlockStyle}">
<Setter Property="FontWeight" Value="Bold"/>
</Style>
</Border.Resources>
<!-- Content -->
<Grid Grid.IsSharedSizeScope="True">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<!-- Column headers -->
<TreeViewItem Grid.Row="0" BorderThickness="1">
<TreeViewItem.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Task"/>
<!-- Placeholders for two columns of ToggleButton -->
<ColumnDefinition SharedSizeGroup="Toggle"/>
<ColumnDefinition SharedSizeGroup="Toggle"/>
<ColumnDefinition SharedSizeGroup="Duration"/>
<ColumnDefinition SharedSizeGroup="Notes"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Task" Style="{StaticResource TextBlockBoldStyle}"/>
<!-- Empty TreeViewItem to measure the size of its ToggleButton into the "Toggle" group-->
<TreeViewItem Grid.Column="1" Padding="0"/>
<TextBlock Grid.Column="3" Text="Duration" Style="{StaticResource TextBlockBoldStyle}"/>
<TextBlock Grid.Column="4" Text="Notes" Style="{StaticResource TextBlockBoldStyle}"/>
</Grid>
</TreeViewItem.Header>
</TreeViewItem>
<!-- Data rows -->
<TreeView Grid.Row="1" x:Name="treeview" ItemsSource="{Binding SubItems}" BorderBrush="Gray" BorderThickness="0 1 0 0">
<TreeView.ItemTemplate>
<!-- Level 0 template leaves space for 2 child "Toggle" levels -->
<HierarchicalDataTemplate ItemsSource="{Binding SubItems}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Task"/>
<ColumnDefinition SharedSizeGroup="Toggle"/>
<ColumnDefinition SharedSizeGroup="Toggle"/>
<ColumnDefinition SharedSizeGroup="Duration"/>
<ColumnDefinition SharedSizeGroup="Notes"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Task}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="3" Text="{Binding Duration}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="4" Text="{Binding Notes}" Style="{StaticResource TextBlockStyle}"/>
</Grid>
<!-- Level 1 template leaves space for 1 child "Toggle" level -->
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding SubItems}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Task"/>
<ColumnDefinition/>
<ColumnDefinition SharedSizeGroup="Toggle"/>
<ColumnDefinition SharedSizeGroup="Duration"/>
<ColumnDefinition SharedSizeGroup="Notes"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Task}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="3" Text="{Binding Duration}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="4" Text="{Binding Notes}" Style="{StaticResource TextBlockStyle}"/>
</Grid>
<!-- Level 2 template has no children -->
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding SubItems}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Task"/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition SharedSizeGroup="Duration"/>
<ColumnDefinition SharedSizeGroup="Notes"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Task}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="3" Text="{Binding Duration}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="4" Text="{Binding Notes}" Style="{StaticResource TextBlockStyle}"/>
</Grid>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
</Border>
</Grid>
</Window>
"#
$inputXML = $inputXML -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^<Win.*', '<Window'
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
[xml]$XAML = $inputXML
#Read XAML
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
try{$Form=[Windows.Markup.XamlReader]::Load( $reader )}
catch [System.Management.Automation.MethodInvocationException] {
Write-Warning "We ran into a problem with the XAML code. Check the syntax for this control..."
write-host $error[0].Exception.Message -ForegroundColor Red
if ($error[0].Exception.Message -like "*button*"){
write-warning "Ensure your <button in the `$inputXML does NOT have a Click=ButtonClick property. PS can't handle this`n`n`n`n"}
}
catch{#if it broke some other way <img draggable="false" class="emoji" alt="π" src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f600.svg">
Write-Host "Unable to load Windows.Markup.XamlReader. Double-check syntax and ensure .net is installed."
}
#===========================================================================
# Store Form Objects In PowerShell
#===========================================================================
$xaml.SelectNodes("//*[#Name]") | %{Set-Variable -Name "WPF$($_.Name)" -Value $Form.FindName($_.Name)}
Function Get-FormVariables{
if ($global:ReadmeDisplay -ne $true){Write-host "If you need to reference this display again, run Get-FormVariables" -ForegroundColor Yellow;$global:ReadmeDisplay=$true}
write-host "Found the following interactable elements from our form" -ForegroundColor Cyan
get-variable WPF*
}
Get-FormVariables
#===========================================================================
# Use this space to add code to the various form elements in your GUI
#===========================================================================
$Data=[pscustomobject]#{Task='Erster Haupteintrag';Duration="10";Notes="notes 1"}
$Child=[pscustomobject]#{Task="Zweite Ebene";Duration="20"; Notes="notes 22"}
$Data2=[pscustomobject]#{Task='Zweiter Haupteintrag';Duration="20";Notes="notes 2"; Subitems=#($Child)}
$Child3=[pscustomobject]#{Task="Dritte Ebene";Duration="35"; Notes="notes 3"}
$Child2=[pscustomobject]#{Task="Zweite Ebene 2";Duration="20"; Notes="notes 222"; Subitems=#($Child3)}
$Data3=[pscustomobject]#{Task='Dritter Haupteintrag';Duration="30";Notes="notes 3"; Subitems=#($Child2)}
$WPFtreeview.items.Add($Data)
$WPFtreeview.items.Add($Data2)
$WPFtreeview.items.Add($Data3)
#Reference
#Adding items to a dropdown/combo box
#$vmpicklistView.items.Add([pscustomobject]#{'VMName'=($_).Name;Status=$_.Status;Other="Yes"})
#Setting the text of a text box to the current PC name
#$WPFtextBox.Text = $env:COMPUTERNAME
#Adding code to a button, so that when clicked, it pings a system
# $WPFbutton.Add_Click({ Test-connection -count 1 -ComputerName $WPFtextBox.Text
# })
#===========================================================================
# Shows the form
#===========================================================================
write-host "To show the form, run the following" -ForegroundColor Cyan
$Form.ShowDialog() | out-null
I have 2 DataGrid to display information from 2 tables I get from Database MySQL. I'm using MVVM pattern and I have 2 ViewModel called: "CheckJigInfoViewModel" and "AccessoryViewModel" for those DataGrid. I setthose ViewModel to my two DataGrid, but only one in two DataGrid can show the data.
Am I getting wrong some code? Please give me some advise!
I atttach some picture for the content above and code for my ViewModel:
The pictures:
- Code for ViewModel:
ViewModel: CheckJigInfoViewModel
class CheckJigInfoViewModel
{
PersonnelBusinessObject personnel;
ObservableCollection<CheckJigInfo> _listCheckJigInfo;
public CheckJigInfoViewModel(){
personnel = new PersonnelBusinessObject();
}
public ObservableCollection<CheckJigInfo> ListCheckJigInfo
{
get
{
_listCheckJigInfo = new ObservableCollection<CheckJigInfo>(personnel.GetListCheckJigInfo());
return _listCheckJigInfo;
}
}
}
ViewModel: AccessoryViewModel
class AccessoryViewModel
{
PersonnelBusinessObject personnel;
ObservableCollection<Accessory> _lstAccessory;
public AccessoryViewModel()
{
personnel = new PersonnelBusinessObject();
}
public ObservableCollection<Accessory> ListAccessory
{
get
{
_lstAccessory = new ObservableCollection<Accessory>(personnel.GetListAccessory());
return _lstAccessory;
}
}
}
the xaml file (AccessoryInfo.xaml):
<Window x:Class="CheckJigWPF.View.AccessoryInfo"
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:CheckJigWPF.View"
xmlns:ViewModels="clr-namespace:CheckJigWPF.ViewModels"
mc:Ignorable="d"
Title="Accessory Info " Height="569.1" Width="890">
<Window.Resources>
<ViewModels:CheckJigInfoViewModel x:Key="CheckJigData"/>
<ViewModels:AccessoryViewModel x:Key="AccessoryData"/>
</Window.Resources>
<!---MAIN GRID-->
<Grid >
<!---MAIN GRID ROWS AND COLUMNS DEFINITION-->
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<!--GROUPBOX1-->
<GroupBox Grid.Row="0" Header="Pulley checking info" Padding="0,2">
<!--SUB GRID FOR GROUPBOX1-->
<Grid>
<!---SUB GRID ROWS AND COLUMNS DEFINITION-->
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<!--GROUPBOX1's Controls-->
<TextBox Grid.Column="0" Name="txtSearch" FontSize="24"/>
<StackPanel Grid.Column="1" Orientation="Horizontal">
<Button Grid.Column="1" Name="btnSearch" Width="100" Content="Search"/>
<Button Grid.Column="1" Name="btnClear" Width="100" Content="Clear"/>
</StackPanel>
<DockPanel Grid.Row="1" Grid.ColumnSpan="2">
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding ListCheckJigInfo, Source={StaticResource CheckJigData}}" ColumnWidth="*" SelectionMode="Single" IsReadOnly="True" FontSize="18" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Width="50" Binding="{Binding ID}"/>
<DataGridTextColumn Header="AUFNR" Width="200" Binding="{Binding AUFNR}"/>
<DataGridTextColumn Header="MATNR" Width="200" Binding="{Binding MATNR}"/>
<DataGridTextColumn Header="Shelf" Width="100" Binding="{Binding Shelf}"/>
<DataGridTextColumn Header="MAKTX" Width="*" Binding="{Binding MAKTX}"/>
<DataGridTextColumn Header="Qty" Width="100" Binding="{Binding Qty}"/>
<DataGridTextColumn Header="MEINS" Width="120" Binding="{Binding MEINS}"/>
<DataGridTextColumn Header="CheckStatus" Width="150" Binding="{Binding Check}"/>
<DataGridTextColumn Header="CheckingDate" Width="180" Binding="{Binding CheckingDate,StringFormat='yyyy-MM-dd HH:mm:ss'}"/>
<DataGridTextColumn Header="User" Width="100" Binding="{Binding UserID}"/>
</DataGrid.Columns>
</DataGrid>
<!--paging-->
<StackPanel Name="pager" Orientation="Horizontal">
<TextBlock Text="{Binding Page,StringFormat=Page 0 of 0}"/>
</StackPanel>
</DockPanel>
<!--End GROUPBOX1's Controls-->
</Grid>
</GroupBox>
<!--END GROUPBOX1-->
<!--GROUPBOX2-->
<GroupBox Grid.Row="1" Header="Accessory Management" Padding="5">
<!--SUB GRID FOR GROUPBOX2-->
<Grid>
<!--GROUPBOX2 ROWS AND COLUMNS DEFINITION-->
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<!--GROUPBOX2's Controls-->
<DockPanel Grid.Row="0" Grid.Column="0">
<StackPanel DockPanel.Dock="Top">
<TextBlock Text="AccessoryID" Margin="5" Padding="5" Background="#FFD7F4AA" FontSize="18"/>
<TextBox Name="txtAccessoryID" Margin="5" FontSize="20"/>
</StackPanel>
<StackPanel DockPanel.Dock="Top">
<TextBlock Text="Accessory Name" Margin="5" Padding="5" Background="#FFD7F4AA" FontSize="18"/>
<TextBox Name="txtAccessoryName" Margin="5" FontSize="20"/>
</StackPanel>
<StackPanel DockPanel.Dock="Top">
<TextBlock Text="Shelf" Margin="5" Padding="5" Background="#FFD7F4AA" FontSize="18"/>
<TextBox Name="txtShelf" Margin="5" FontSize="20"/>
</StackPanel>
</DockPanel>
<!--SUB GRID FOR GROUPBOX2-->
<Grid Grid.Column="1">
<!--GROUPBOX2 ROWS AND COLUMNS DEFINITION-->
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<!--GROUPBOX2's Controls-->
<TextBox Grid.Column="0" Name="txtSearchAcs" FontSize="24"/>
<StackPanel Grid.Column="1" Orientation="Horizontal">
<Button Name="btnSearchAcs" Width="100" Content="Search"/>
<Button Name="btnClearAcs" Width="100" Content="Clear"/>
</StackPanel>
<DockPanel Grid.Row="1" Grid.ColumnSpan="2">
<DataGrid Name="dgvAccessoryInfo" ItemsSource="{Binding ListAccessory, Source ={StaticResource AccessoryData}}" AutoGenerateColumns="False" ColumnWidth="*" SelectionMode="Single" IsReadOnly="True" FontSize="18" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<DataGrid.Columns>
<DataGridTextColumn Header="Acs ID" Binding="{Binding ACSID}"/>
<DataGridTextColumn Header="Acs Name" Binding="{Binding ACSName}"/>
<DataGridTextColumn Header="Acs Shelf" Binding="{Binding ACSShelf}"/>
</DataGrid.Columns>
</DataGrid>
</DockPanel>
<!--End GROUPBOX2's Controls-->
</Grid>
<!--FUNCTIONS-->
<StackPanel Grid.Row="1" Grid.Column="0" Orientation="Horizontal">
<Button Content="Save" Width="100" Height="40" BorderThickness="0" Background="MediumSeaGreen" Foreground="#ffffff" FontSize="24" Margin="5"/>
</StackPanel>
</Grid>
</GroupBox>
<!--END GROUPBOX 2-->
</Grid>
</Window>
I expect to show each ViewModel for each DataGrid.
Thanks in advance!
I found the answer for my problem. Thanks #Avinash Reddy for your advise. Because my sub-grid layout for the DataGrid in my GroupBox2 overflow. It drowned the GridData of GroupBox1 and I didn't see my GridData in GroupBox1 shown the data. I fixed it by to set a "Height" for the DataGrid in GroupBox2 and It's worked well.
I have 3 Grids - 1 master grid that houses 2 child grids. I want ScrollViewer to appear in just 1 of the child grids. How can this be done? The ScrollViewer either doesn't show up (when set to auto), or it shows up but is disabled with no position indicator (when set to visible).
I'm able to apply it to the master grid and it works, but not the child grid. All heights are set to auto in the case of master grid, so I assume the same settings will work for child grid as well, but it didn't. Here are the extracts of my codes currently:
<UserControl x:Class="StoreMgmtSys.Views.IssueInfoPage" Height="Auto" Width="Auto">
<Grid x:Name="layoutGridMaster" Height="Auto" Width="Auto" ShowGridLines="False">
<ScrollViewer CanContentScroll="False" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<Grid x:Name="gridEqpItem" Height="Auto" Grid.Row="0" Grid.Column="0">
</Grid>
</ScrollViewer>
<Grid x:Name="layoutGridIssue" Grid.Row="0" Grid.Column="1" Margin="0,0,0,0">
</Grid>
</Grid>
</UserControl>
Edit - Appendix for people who wants to know the content of my grid with the ScrollViewer:
<ScrollViewer CanContentScroll="False" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<Grid x:Name="gridEqpItem" Background="PaleVioletRed" Height="Auto" Grid.Row="0" Grid.Column="0" Margin="0,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<telerik:RadGridView Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Margin="2,2,2,0"
x:Name="gridCommsEqp" CanUserDeleteRows="False" ItemsSource="{Binding}"
AutoGenerateColumns="False" CanUserInsertRows="False" MaxHeight="500"
ColumnWidth="Auto" CanUserResizeColumns="False" CanUserResizeRows="True"
HorizontalAlignment="Left" VerticalAlignment="Top" MaxWidth="556"
IsReadOnly="True" SelectionChanged="gridCommsEqp_OnSelectionChanged"
Width="Auto" ShowGroupPanel="False" RowIndicatorVisibility="Collapsed"
FilteringMode="FilterRow" FilterOperatorsLoading="Grid_OnFilterOperatorsLoading">
<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn DataMemberBinding="{Binding CommonDesc}" UniqueName="CommonDesc"
TextWrapping="Wrap" Width="190" TextAlignment="Center">
<telerik:GridViewDataColumn.Header>
<TextBlock Text="Equipment" TextAlignment="Center" />
</telerik:GridViewDataColumn.Header>
</telerik:GridViewDataColumn>
<telerik:GridViewDataColumn DataMemberBinding="{Binding OtherKeywords}"
UniqueName="OtherKeywords"
TextWrapping="Wrap" Width="210" TextAlignment="Center">
<telerik:GridViewDataColumn.Header>
<TextBlock Text="Keywords" TextAlignment="Center" />
</telerik:GridViewDataColumn.Header>
</telerik:GridViewDataColumn>
<telerik:GridViewDataColumn DataMemberBinding="{Binding CurrentQty}" MaxWidth="73"
TextAlignment="Center"
UniqueName="CurrentQty" TextWrapping="Wrap">
<telerik:GridViewDataColumn.Header>
<TextBlock Text="Current Quantity" TextWrapping="Wrap" TextAlignment="Center" />
</telerik:GridViewDataColumn.Header>
</telerik:GridViewDataColumn>
<telerik:GridViewDataColumn DataMemberBinding="{Binding TotalQty}" TextAlignment="Center"
UniqueName="TotalQty" TextWrapping="Wrap" MaxWidth="73">
<telerik:GridViewDataColumn.Header>
<TextBlock Text="Total Quantity" TextWrapping="Wrap" TextAlignment="Center" />
</telerik:GridViewDataColumn.Header>
</telerik:GridViewDataColumn>
</telerik:RadGridView.Columns>
</telerik:RadGridView>
<telerik:RadListBox Margin="5,5,5,0" Grid.Column="0" Grid.Row="1" Grid.RowSpan="2"
x:Name="listBoxItems" HorizontalAlignment="Left" VerticalAlignment="Top"
Width="250" SelectionMode="Multiple" Visibility="Collapsed"
TextPath="Serial" MaxHeight="400" Background="Transparent"
ItemContainerStyle="{StaticResource MetroListBoxItem}"
SelectionChanged="ListBoxItems_OnSelectionChanged">
<telerik:RadListBox.DragVisualProvider>
<telerik:ScreenshotDragVisualProvider />
</telerik:RadListBox.DragVisualProvider>
<telerik:RadListBox.DragDropBehavior>
<telerik:ListBoxDragDropBehavior />
</telerik:RadListBox.DragDropBehavior>
<telerik:RadListBox.DataConverter>
<storeMgmtSys:ItemToIssueConverter />
</telerik:RadListBox.DataConverter>
<telerik:RadListBox.AllowDrop>False</telerik:RadListBox.AllowDrop>
</telerik:RadListBox>
<telerik:RadGridView Grid.Row="1" Grid.Column="1" Margin="0,5,5,0"
FontWeight="Bold" FontSize="14"
x:Name="gridCommsEqpSet" CanUserDeleteRows="False" ItemsSource="{Binding}"
AutoGenerateColumns="False" CanUserInsertRows="False" MaxHeight="400"
Height="Auto" HorizontalAlignment="Left" VerticalAlignment="Top"
ColumnWidth="Auto" CanUserResizeColumns="False" CanUserResizeRows="True"
MaxWidth="300" Width="Auto" ShowGroupPanel="False" SelectionMode="Single"
RowIndicatorVisibility="Collapsed" Visibility="Collapsed"
SelectionChanged="GridCommsEqpSet_OnSelectionChanged"
FilterOperatorsLoading="Grid_OnFilterOperatorsLoading">
<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn DataMemberBinding="{Binding Description}" UniqueName="Description"
TextWrapping="Wrap" Header="SELECT A SET" Width="Auto"
MaxWidth="200" HeaderTextAlignment="Center"
IsReadOnly="True" TextAlignment="Center"/>
</telerik:RadGridView.Columns>
</telerik:RadGridView>
<telerik:RadListBox Margin="0,5,5,0" Grid.Column="1" Grid.Row="2"
x:Name="listBoxSetItems" HorizontalAlignment="Left" VerticalAlignment="Top"
Width="250" ItemContainerStyle="{StaticResource MetroListBoxItem}"
SelectionMode="Multiple" Background="Transparent"
TextPath="Serial" MaxHeight="400" Visibility="Collapsed">
<telerik:RadListBox.DragVisualProvider>
<telerik:ScreenshotDragVisualProvider />
</telerik:RadListBox.DragVisualProvider>
<telerik:RadListBox.DragDropBehavior>
<telerik:ListBoxDragDropBehavior />
</telerik:RadListBox.DragDropBehavior>
<telerik:RadListBox.DataConverter>
<storeMgmtSys:ItemToIssueConverter />
</telerik:RadListBox.DataConverter>
<telerik:RadListBox.AllowDrop>False</telerik:RadListBox.AllowDrop>
</telerik:RadListBox>
<Label Grid.Column="0" Grid.Row="3" Margin="5,5,5,5" VerticalAlignment="Top" HorizontalAlignment="Left"
Content="Issue Date" FontWeight="Bold" Foreground="#FF008BFF" FontSize="14" />
<Label Grid.Column="0" Grid.Row="4" Margin="5,0,5,5" VerticalAlignment="Top" HorizontalAlignment="Left"
Content="Receipt Date" FontWeight="Bold" Foreground="#FF008BFF" FontSize="14" />
<telerik:RadDatePicker Grid.Column="0" Grid.Row="3" Margin="98,5,0,0"
HorizontalAlignment="Left" VerticalAlignment="Top"
x:Name="issueDate" Height="30" Width="155"
SelectionChanged="issueDate_SelectionChanged" />
<telerik:RadDatePicker Grid.Column="0" Grid.Row="4" Margin="98,0,0,0"
HorizontalAlignment="Left" VerticalAlignment="Top"
x:Name="receiptDate" Height="30" Width="155"
SelectionChanged="receiptDate_SelectionChanged"/>
</Grid>
</ScrollViewer>
Finally solved my problem. The problem lies with the RowDefinition of my master grid (I did not reveal the codes in my question). The trick is to set the first RowDefinition Height to "*" as shown below. Initially my code was "Auto". Don't ask me why though; I don't know how to explain well as my programming concepts are not so good yet.
<Grid x:Name="layoutGridMaster" Height="Auto" Width="Auto" ShowGridLines="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
.
.
.
</Grid>
I have a DataGrid and a grid on my WPF application and I want their respected columns be the same size. I thought maybe I use SharedSizeGroup but it seems that DataGrid columns donβt have this property (Am I wrong?).
How can I do this?
Update1
I changed the xaml as follow:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20" MaxHeight="20" MinHeight="20" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid >
<Grid.ColumnDefinitions >
<ColumnDefinition Width="{Binding ElementName=titleDataGridColumn, Path=ActualWidth}" />
<ColumnDefinition Width="{Binding ElementName=forenameDataGridColumn, Path=ActualWidth}"/>
<ColumnDefinition Width="{Binding ElementName=surnameDataGridColumn, Path=ActualWidth}" />
<ColumnDefinition Width="{Binding ElementName=dobDataGridColumn, Path=ActualWidth}" />
</Grid.ColumnDefinitions>
<Button Content="Clear" HorizontalAlignment="Stretch" Name="searchClearButton" VerticalAlignment="Stretch" Grid.Column="0" Click="searchClearButton_Click" />
<TextBox HorizontalAlignment="Stretch" Name="forenameTextBox" VerticalAlignment="Stretch" Grid.Column="1" />
<TextBox HorizontalAlignment="Stretch" Name="surnameTextBox" VerticalAlignment="Stretch" Grid.Column="2" />
<TextBox HorizontalAlignment="Stretch" Name="dobTextBox" VerticalAlignment="Stretch" Grid.Column="3" />
</Grid>
<DataGrid Grid.Row="1" x:Name="DataDataGrid" HorizontalAlignment="Stretch" AutoGenerateColumns="False" VerticalAlignment="Stretch" ItemsSource="{Binding}" AlternationCount="1" IsReadOnly="True" SelectionMode="Single" >
<DataGrid.Columns>
<DataGridTextColumn x:Name="titleDataGridColumn" Binding="{Binding Path='Title'}" Header="Title" Width="35*" />
<DataGridTextColumn x:Name="forenameDataGridColumn" Binding="{Binding Path='Forename'}" Header="Forename" Width="65*" />
<DataGridTextColumn x:Name="surenameDataGridColumn" Binding="{Binding Path='Surname'}" Header="Surname" Width="65*"/>
<DataGridTextColumn x:Name="dobDataGridColumn" Binding="{Binding DOB,StringFormat={}\{0:dd/MM/yyyy\}}" Header="DOB" Width="70*"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
But now the surname column in grid is bigger than its corresponding column in datagrid and the button is a bit to left ( could be fixed by setting margin on grid column) but I cannot understand why surname columns are not same size?
Update 2
This is working (Problem was the surenameDataGridColumn was not correct (spelling))
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20" MaxHeight="20" MinHeight="20" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid >
<Grid.ColumnDefinitions >
<ColumnDefinition Width="6px"/>
<ColumnDefinition Width="{Binding ElementName=titleDataGridColumn, Path=ActualWidth}" />
<ColumnDefinition Width="{Binding ElementName=forenameDataGridColumn, Path=ActualWidth}"/>
<ColumnDefinition Width="{Binding ElementName=surnameDataGridColumn, Path=ActualWidth}" />
<ColumnDefinition Width="{Binding ElementName=dobDataGridColumn, Path=ActualWidth}" />
</Grid.ColumnDefinitions>
<Button Content="Clear" HorizontalAlignment="Stretch" Name="searchClearButton" VerticalAlignment="Stretch" Grid.Column="1" Click="searchClearButton_Click" />
<TextBox HorizontalAlignment="Stretch" Name="forenameTextBox" VerticalAlignment="Stretch" Grid.Column="2" />
<TextBox HorizontalAlignment="Stretch" Name="surnameTextBox" VerticalAlignment="Stretch" Grid.Column="3" />
<TextBox HorizontalAlignment="Stretch" Name="dobTextBox" VerticalAlignment="Stretch" Grid.Column="4" />
</Grid>
<DataGrid Grid.Row="1" x:Name="DataGrid" HorizontalAlignment="Stretch" AutoGenerateColumns="False" VerticalAlignment="Stretch" ItemsSource="{Binding}" AlternationCount="1" IsReadOnly="True" >
<DataGrid.Columns>
<DataGridTextColumn x:Name="titleDataGridColumn" Binding="{Binding Path='Title'}" Header="Title" Width="35*" />
<DataGridTextColumn x:Name="forenameDataGridColumn" Binding="{Binding Path='Forename'}" Header="Forename" Width="65*" />
<DataGridTextColumn x:Name="surnameDataGridColumn" Binding="{Binding Path='Surname'}" Header="Surname" Width="65*"/>
<DataGridTextColumn x:Name="dobDataGridColumn" Binding="{Binding DOB,StringFormat={}\{0:dd/MM/yyyy\}}" Header="DOB" Width="70*"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
By adding a new column, I aligned the left of button with the left of Title column in DataGrid.
Use binding to bind the Grid column widths to the size of the columns in your DataGrid. For example:
<dg:DataGridTextColumn x:Name="FirstColumn" Header="Text"/>
<ColumnDefinition Width="{Binding ElementName=FirstColumn, Path=ActualWidth}"/>