I have tried binding a CheckBox IsChecked to IsSelected of a ListBoxItem in xaml.
This way I can populate a dynamic list with corresponding checkboxes and I receive a list of checked items using the form.
So far so good. But, how do I precheck items in the list programmatically, preferable in PowerShell?
I have been testing with a XAML that looks like this:
<Window x:Class="WpfHandler.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:WpfHandler"
mc:Ignorable="d"
Title="Testing" Height="300" Width="250">
<Window.Resources>
<Style TargetType="ListBoxItem">
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<CheckBox Margin="5,2" IsChecked="{Binding IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource TemplatedParent}}">
<ContentPresenter />
</CheckBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Border Padding="10">
<Grid Margin="0 10 0 10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Name="Testing" Orientation="Horizontal">
<ListBox Name="Box_Test" ItemsSource="{DynamicResource test}" Height="220" SelectionMode="Multiple"/>
</StackPanel>
</Grid>
</Border>
</Window>
...and a Powershell script that looks like this:
add-type -AssemblyName PresentationFramework
Add-Type -AssemblyName System.Drawing
$xamlFile = "testing.xaml"
[xml]$xaml = (Get-Content $xamlFile -Raw) -replace 'x:Name', 'Name'
$xaml.Window.RemoveAttribute('x:Class')
$xaml.Window.RemoveAttribute('mc:Ignorable')
$xaml.SelectNodes("//*") | ForEach-Object {
$_.RemoveAttribute('d:LayoutOverrides')
}
$reader = (New-Object System.Xml.XmlNodeReader $xaml)
try {
$Script:Form = [Windows.Markup.XamlReader]::Load( $reader )
# Get Name variables from form and set scope to script
$xaml.SelectNodes("//*[#Name]") | ForEach-Object { Set-Variable -Name ($_.Name) -Value $Script:Form.FindName($_.Name) -Scope script }
}
catch {
Write-Host "Unable to load Windows.Markup.XamlReader";
exit
}
$testdata = Get-Content "testdata.txt"
$Script:Form.Resources.Add("test", $testdata)
$Script:Form.ShowDialog()
Here is an example to select the first two items by index:
$Script:Form.Add_Loaded({
foreach( $i in 0, 1 ) {
$Box_Test.ItemContainerGenerator.ContainerFromIndex( $i ).IsSelected = $true
}
})
$Script:Form.ShowDialog()
ItemContainerGenerator is a property of the ItemsControl base class of ListBox.
Related
I tried to set the cell style for the grid control column within Lookupedit. but not applied at run time, but if I remove any property of the grid control column in debug mode at the time style applied, cell style should be applied when binding the data source, any idea to fix this, below is the code snippet
Thanks in advance,
Pandiyan Thangarasu
<UserControl x:Class="VisualizeWorkFlow.Legends"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid"
xmlns:dxc="http://schemas.devexpress.com/winfx/2008/xaml/core"
xmlns:dxb="http://schemas.devexpress.com/winfx/2008/xaml/bars"
xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors"
xmlns:dxgt="http://schemas.devexpress.com/winfx/2008/xaml/grid/themekeys"
xmlns:local="clr-namespace:VisualizeWorkFlow"
mc:Ignorable="d"
BorderBrush="LightGray">
<UserControl.Resources>
<local:LegendColorCodeConverterClass x:Key="LegendColorCodeConverter"/>
</UserControl.Resources>
<Grid Background="White">
<dxg:LookUpEdit DisplayMember="Name" dxc:ThemeManager.ThemeName="Office2010Silver" Name="PART_Editor" Width="300" >
<dxg:LookUpEdit.PopupContentTemplate>
<ControlTemplate>
<dxg:GridControl Name="PART_GridControl" CustomColumnDisplayText="PART_GridControl_CustomColumnDisplayText">
<dxg:GridControl.Columns>
<dxg:GridColumn Header="Name" Width="150" FieldName="Name" />
<dxg:GridColumn Header="Color Code" Width="30" FieldName="ColorCode">
<dxg:GridColumn.CellStyle>
<Style BasedOn="{StaticResource {dxgt:GridRowThemeKey ResourceKey=CellStyle}}" TargetType="dxg:CellContentPresenter">
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Background" Value="{Binding Value, Converter={StaticResource LegendColorCodeConverter}}"/>
</Style>
</dxg:GridColumn.CellStyle>
</dxg:GridColumn>
</dxg:GridControl.Columns>
<dxg:GridControl.View>
<dxg:TreeListView KeyFieldName="ID" ParentFieldName="ParentID" ShowColumnHeaders="False" ShowIndicator="False" AutoExpandAllNodes="True"/>
</dxg:GridControl.View>
</dxg:GridControl>
</ControlTemplate>
</dxg:LookUpEdit.PopupContentTemplate>
</dxg:LookUpEdit>
</Grid>
</UserControl>
I'm trying to create a RDP manager for myself. Since a RDP manager adds and closes tabs dynamically during the use I need this to work properly in WPF.
If I add a function (method) to the button, inside the tabitem template, it works perfectly fine in visual studio. When i copy over the XAML to my PowerShell script, I cannot run a function when that button is being pressed. Since the button is inside a template i have no way to access the button controls inside the PowerShell script to create an add_click({ #somecode }).
This is my WPF tabcontrol:
<TabControl Name="MainTabControlRDPPages" ItemsSource="{Binding Tabs}">
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate DataType="local:TabViewModel">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding}"/>
<Button Grid.Column="1" x:Name="button_close" Click="RunPowershellTest">
<Button.Template>
<ControlTemplate TargetType="Button">
<Path Data="M0,0 L8,8 M8,0 L0,8" StrokeThickness="3" VerticalAlignment="Center" Margin="5,4,0,2">
<Path.Style>
<Style TargetType="{x:Type Path}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Stroke" Value="LightGray" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Stroke" Value="Black" />
</Trigger>
</Style.Triggers>
</Style>
</Path.Style>
</Path>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.ItemContainerStyle>
<TabItem Header="No Session" />
</TabControl>
I'm using this code in PowerShell to read all the controls and give them a name.
$reader =(New-Object System.Xml.XmlNodeReader $xaml)
$Window = [Windows.Markup.XamlReader]::Load( $reader )
$xaml.SelectNodes("//*[#Name]") | ForEach-Object { Set-Variable -Name
($_.Name) -Value $window.FindName($_.Name) -Scope Script }
How do I make sure I can create a click event for the button_close button? If there's an option to manually add a WPF formatted tabitem during Powershell execution without specifying a template I'm fine with that too.
UPDATE
The button only needs to close the tabpage. If this can be done within XAML i'm fine with that.
Thanks to the help of someone inside a PowerShell facebook group I have the solution below. This creates a handler for the tabcontrol where it checks if a button was pressed inside the header. If yes, proceed with closing the tabpage.
[System.Windows.RoutedEventHandler]$EventontabControl = {
$script:headerName = $_.OriginalSource
Write-Host $headername
$script:MainTabControlRDPPageToClose = $_.Source
$MainTabControlRDPPages.Items.Remove($MainTabControlRDPPageToClose)
}
$MainTabControlRDPPages.AddHandler([System.Windows.Controls.Button]::ClickEvent, $EventontabControl)
I have been trying to develop a bar of RadioButtons (styled as ToggleButtons) which select which content is displayed in a ContentControl. I have been able to use a DataTrigger to display the proper view in the ContentControl based on which RadioButton is checked, but I am also trying to bind a view model from the parent DataContext into the child's DataContext, without success. A minimum sample is as follows:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" >
<RadioButton Name="rbShowChild" Content="Show Child" Style="{StaticResource {x:Type ToggleButton}}" />
</StackPanel>
<ContentControl Grid.Row="1">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, ElementName=rbShowChild}" Value="True">
<Setter Property="Content">
<Setter.Value>
<local:ChildView DataContext="{Binding ChildViewModel, PresentationTraceSources.TraceLevel=High}"/>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</Grid>
</Window>
The Binding to the ChildViewModel seems to be the part that is not working as expected. For completeness, here is the related ChildView.
<UserControl x:Class="WpfApplication1.ChildView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<StackPanel>
<TextBlock Text="Child" />
<TextBlock Text="{Binding Text}" />
</StackPanel>
</Grid>
</UserControl>
And the ViewModels:
namespace WpfApplication1
{
public class ChildViewModel
{
public string Text { get; set; }
public ChildViewModel()
{
Text = "It works!";
}
}
public class MainWindowViewModel
{
public ChildViewModel ChildViewModel { get; set; }
public MainWindowViewModel()
{
ChildViewModel = new ChildViewModel();
}
}
}
And the MainWindow DataContext is set as follows:
var window = new MainWindow()
{
DataContext = new MainWindowViewModel()
};
window.Show();
The Output window (with PresentationTraceSources.TraceLevel=High) shows this:
System.Windows.Data Error: 3 : Cannot find element that provides DataContext. BindingExpression:Path=ChildViewModel; DataItem=null; target element is 'ChildView' (Name=''); target property is 'DataContext' (type 'Object')
which leads me to think that somehow the MainWindow.DataContext is not the DataContext used to resolve the "{Binding ChildViewModel}" expression in the trigger, but I couldn't find any DataContext property on the trigger, nor have I been able to find anything in my searches to suggest an answer for this.
I would very much appreciate any suggestions to resolve this.
UPDATE: Looks like the issue is with how you are setting the data context.
Set the DataContext of the window in the XAML, then set the data context of the ContentControl to the ChildViewModel. Then set the actual displayed Content (AKA ChildView) Value. The ContentControl is the host of this view, so it should be the DataContext supplier. I myself am a little unsure the exact mechanics going on, but I know for fact the below code works.
NOTE Your XML namespaces may vary from the code below. Adjust accordingly.
<Window.DataContext>
<!-- Data Context added here OR in code-behind initialize-->
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" >
<RadioButton Name="rbShowChild" Content="Show Child" Style="{StaticResource {x:Type ToggleButton}}" />
</StackPanel>
<!-- Bind the ContentControls Data context-->
<ContentControl DataContext="{Binding ChildViewModel}" Grid.Row="1">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, ElementName=rbShowChild}" Value="True">
<Setter Property="Content">
<Setter.Value>
<!-- Just simply set the view content here, no binding.-->
<local:ChildView />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</Grid>
Doing this, I have run your application just fine in a project.
I have two combo boxes: CarTypeComboBox and SeriesComboBox.
Issues:
1. I want the SeriesCombox to be visible only when the user select BMW.
2. System.Windows.Style is showing up in SeriesComboBox.
Thank you
Complete Code:
<Window x:Class="StyleTrigger.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:StyleTrigger"
xmlns:local2="clr-namespace:ComboBoxData"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources >
<local2:ComboBoxItemCollection x:Key="CarItemsCollection"/>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50*" />
<ColumnDefinition Width="50*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="1" Grid.Column="0" >
<Label x:Name="CarBrand" Height="30" Width="75" Margin="10,0,0,0" Content="Car Brand"
HorizontalAlignment="Left" VerticalAlignment="Top"/>
<ComboBox x:Name="CarTypeComboBox" Margin="10,0,0,0" Width="100" Height="30" HorizontalAlignment="Left" VerticalAlignment="Top"
ItemsSource="{Binding Mode=OneWay, Source={StaticResource CarItemsCollection}}"
DisplayMemberPath="CarType"
SelectedValuePath="CarID"
/>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="1" >
<Label x:Name="CarSeries" Height="30" Width="75" Margin="10,0,0,0" Content="Car Series"
HorizontalAlignment="Left" VerticalAlignment="Top"/>
<ComboBox x:Name="SeriesComboBox" Margin="10,0,0,0" Width="100" Height="30"
HorizontalAlignment="Left" VerticalAlignment="Top">
<sys:String>230</sys:String>
<sys:String>280</sys:String>
<sys:String>530</sys:String>
<Style TargetType="ComboBox">
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedItem.CarType, ElementName=CarTypeComboBox}" Value="BMW">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox>
</StackPanel>
</Grid>
</Window>
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ComboBoxData
{
class SingleComboBoxItem
{
public SingleComboBoxItem(int pCarID,String pCarBrand)
{
CarID = pCarID;
CarType = pCarBrand;
}
public string CarType { get; set; }
public int CarID { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ComboBoxData
{
class ComboBoxItemCollection : ObservableCollection<SingleComboBoxItem>
{
public ComboBoxItemCollection() : base()
{
Add(new SingleComboBoxItem(1,"Honda"));
Add(new SingleComboBoxItem(2,"Toyota"));
Add(new SingleComboBoxItem(3,"BMW"));
Add(new SingleComboBoxItem(4,"Dodge"));
Add(new SingleComboBoxItem(5,"Lexus"));
}
}
}
Add an additional Setter with the default value Hidden to the Style itself, not the triggers.
Your ComboBox style should look like this:
<Style TargetType="ComboBox">
<!-- Just add this one Setter -->
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedItem.CarType, ElementName=CarTypeComboBox}" Value="BMW">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
Do not set the Visibility attribute on the ComboBox tag itself. No Visibility="Hidden" on the ComboBox. That will override the style setters and it will never be visible. ONLY set Visibility in setters in the style.
UPDATE
Now that I've seen the whole code, I can offer a little more insight. First, you said "The SeriesComboBox is not appearing when I select BMW.", but what's happening in the version you just posted is that it is not disappearing when you don't select BMW. Now, let's take a look at what it does do:
There's an anomaly in that dropdown list: The last item is System.Windows.Style. I'm willing to bet you haven't ever seen that model of BMW on the road any more than I have.
Your Style is correctly defined, and I think it may have been correct even before we started hassling you about it. The trouble is you aren't assigning it to the Style property of the ComboBox. Instead you're adding it to the default content property, which in the case of ComboBox is Items. In WPF, you can throw literally almost figuratively just about anything at all in a ComboBox (or ListBox) items collection. It's a collection of object. It'll eat any old garbage you feed it and never complain. Since you didn't specify DisplayMemberPath for that one, it just cheerfully calls ToString() on each object in turn.
So to assign the style to the Style property of the ComboBox, put it inside <ComboBox.Style>:
<ComboBox
x:Name="SeriesComboBox"
Margin="10,0,0,0"
Width="100"
Height="30"
HorizontalAlignment="Left"
VerticalAlignment="Top">
<sys:String>230</sys:String>
<sys:String>280</sys:String>
<sys:String>530</sys:String>
<ComboBox.Style>
<Style TargetType="ComboBox">
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
<DataTrigger
Binding="{Binding SelectedItem.CarType, ElementName=CarTypeComboBox}"
Value="BMW"
>
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
You could also define the Style in Resources, give it x:Key="SeriesComboBoxStyle", and set the Style attribute on the ComboBox tag: Style="{StaticResource SeriesComboBoxStyle}".
The text is grayed out when the DatePicker is disabled and I want the content to be easier to read.
What I did on some TextBoxes was:
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="Black" />
</Trigger>
</Style.Triggers>
</Style>
It did make the text easier to read.
I do manage to change the Foreground colour on the DataPicker but it does not do the trick. The text was still grayed out.
Seems like there is another property I need to set to make the content of the disabled DatePicker easier to read.
So, how do I make the content of my disabled DatePicker easier to read?
Can you extend DatePicker by adding bool DependencyProperty called Editable.
I found a working example at the following link, note that I run this code in .NET 4.
Here is the DatePicker Control:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
namespace DatePickerStyle
{
public class ExtendedDatePicker : DatePicker
{
public static readonly DependencyProperty EditableProperty = DependencyProperty.Register("Editable", typeof(bool),
typeof(ExtendedDatePicker), new PropertyMetadata(true));
public bool Editable
{
get { return (bool)GetValue(EditableProperty); }
set { SetValue(EditableProperty, value); }
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var textBox = GetTemplateChild("PART_TextBox") as DatePickerTextBox;
var binding = new Binding { Source = this, Path = new PropertyPath(ExtendedDatePicker.EditableProperty) };
textBox.SetBinding(UIElement.FocusableProperty, binding);
}
}
}
Here is the XAML:
<Window x:Class="DatePickerStyle.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:DatePickerStyle="clr-namespace:DatePickerStyle"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<DatePicker IsEnabled="True" Grid.Row="0" SelectedDate="2002/12/31"/>
<DatePicker IsEnabled="False" Grid.Row="1" SelectedDate="2002/12/31"/>
<DatePickerStyle:ExtendedDatePicker Editable="True" Grid.Row="2" SelectedDate="2002/12/31"/>
<DatePickerStyle:ExtendedDatePicker Editable="False" Grid.Row="3" SelectedDate="2002/12/31"/>
</Grid>
</Window>
My I suggest this simpler, universal approach?
<ControlTemplate x:Key="MyDisabledDatePicker">
<Border BorderBrush="Black" BorderThickness="1">
<TextBlock
Text="{Binding Path=SelectedDate, StringFormat={}{0:d}, RelativeSource={RelativeSource TemplatedParent}}"
VerticalAlignment="Center" HorizontalAlignment="Left" Padding="10,0,0,0"/>
</Border>
</ControlTemplate>
<Style TargetType="{x:Type DatePicker}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsEnabled, RelativeSource={RelativeSource Self}}" Value="false">
<Setter Property="Template" Value="{StaticResource MyDisabledDatePicker}" />
</DataTrigger>
</Style.Triggers>
</Style>
Presto!
the above code w ExtendedDatePicker works, first I thought it didn't but that was because the dropdown could still change the text and the Editable="False" doesn't work on the dropdown
so don't forget to add the following to the ExtendedDatePicker
Editable="False" AllowDrop="False" IsDropDownOpen="False" IsHitTestVisible="False" IsManipulationEnabled="False"