This is my first time trying to databind an ICommand. I have a digital LED control that I would like to act like a Button, so I changed the DataTemplate for a Button control to look like an LED:
LED.xaml
<UserControl x:Class="LedControlDatabindingTest.LED"
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"
x:Name="root"
mc:Ignorable="d"
Height="Auto" Width="Auto">
<Grid DataContext="{Binding ElementName=root}">
<StackPanel Orientation="{Binding LEDOrientation, FallbackValue=Vertical}">
<!-- LED portion -->
<Button BorderBrush="Transparent" Background="Transparent" Click="Button_Click">
<Button.ContentTemplate>
<DataTemplate>
<Grid>
<Ellipse Grid.Column="0" Margin="3" Height="{Binding ElementName=root, Path=LEDSize, FallbackValue=16}"
Width="{Binding ElementName=root, Path=LEDSize, FallbackValue=16}"
Fill="{Binding ElementName=root, Path=LEDColor, FallbackValue=Green}"
StrokeThickness="2" Stroke="DarkGray" HorizontalAlignment="Center" />
<Ellipse Grid.Column="0" Margin="3" Height="{Binding ElementName=root, Path=LEDSize, FallbackValue=16}"
Width="{Binding ElementName=root, Path=LEDSize, FallbackValue=16}" HorizontalAlignment="Center">
<Ellipse.Fill>
<RadialGradientBrush GradientOrigin="0.5,1.0">
<RadialGradientBrush.RelativeTransform>
<TransformGroup>
<ScaleTransform CenterX="0.5" CenterY="0.5" ScaleX="1.5" ScaleY="1.5"/>
<TranslateTransform X="0.02" Y="0.3"/>
</TransformGroup>
</RadialGradientBrush.RelativeTransform>
<GradientStop Offset="1" Color="#00000000"/>
<GradientStop Offset="0.4" Color="#FFFFFFFF"/>
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
</Grid>
</DataTemplate>
</Button.ContentTemplate>
</Button>
<!-- label -->
<TextBlock Grid.Column="1" Margin="3" HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding LEDLabel, FallbackValue=0}" />
</StackPanel>
</Grid>
</UserControl>
I want the host application to be able to databind to properties like size, color, and label of the LED. In addition, I want to be able to bind to a command handler.
LED.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace LedControlDatabindingTest {
/// <summary>
/// Interaction logic for LED.xaml
/// </summary>
public partial class LED : UserControl
{
public static DependencyProperty LEDColorProperty = DependencyProperty.Register( "LEDColor", typeof(Brush), typeof(LED));
public Brush LEDColor
{
get { return this.GetValue(LEDColorProperty) as Brush; }
set {
this.SetValue( LEDColorProperty, value);
}
}
public static DependencyProperty LEDSizeProperty = DependencyProperty.Register( "LEDSize", typeof(int), typeof(LED));
public int LEDSize
{
get { return (int)GetValue(LEDSizeProperty); }
set {
SetValue( LEDSizeProperty, value);
}
}
public static DependencyProperty LEDLabelProperty = DependencyProperty.Register( "LEDLabel", typeof(string), typeof(LED));
public string LEDLabel
{
get { return (string)GetValue(LEDLabelProperty); }
set {
SetValue( LEDLabelProperty, value);
}
}
public static DependencyProperty LEDOrientationProperty = DependencyProperty.Register( "LEDOrientation", typeof(Orientation), typeof(LED));
public Orientation LEDOrientation
{
get { return (Orientation)GetValue(LEDOrientationProperty); }
set {
SetValue( LEDOrientationProperty, value);
}
}
public static readonly DependencyProperty LEDClickedProperty = DependencyProperty.Register("LEDClicked", typeof(ICommand), typeof(LED), new PropertyMetadata(null));
public ICommand LEDClicked
{
get { return (ICommand)GetValue(LEDClickedProperty); }
set { SetValue( LEDClickedProperty, value); }
}
public LED()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
LEDClicked.Execute( null);
}
}
}
My test application is simple.
MainWindow.xaml:
<Window x:Class="LedControlDatabindingTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:LedControlDatabindingTest"
Title="MainWindow" Height="70" Width="250">
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Digital Inputs:" />
<ListBox ItemsSource="{Binding AvailableDigitalInputs}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel IsItemsHost="True" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<local:LED LEDLabel="{Binding Index}" LEDColor="{Binding Color}" LEDSize="12" LEDClicked="{Binding Clicked}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</StackPanel>
</Window>
There are so no-nos in my code-behind, like the DataContext for my application, but I think for the purposes of this demo it's okay for now.
MainWindow.xaml.cs:
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.CommandWpf;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace LedControlDatabindingTest {
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private class DigitalInputData : ViewModelBase
{
private Brush _color;
public Brush Color
{
get { return _color; }
set {
_color = value;
RaisePropertyChanged();
}
}
public int Index { get; set; }
public ICommand Clicked { get; set; }
private bool _state;
public DigitalInputData( int index, Brush on_color)
{
Index = index;
Color = Brushes.LightGray;
Clicked = new RelayCommand( () => {
// get current state of this digital input and then toggle it
_state = !_state;
// read back and update here until I get threaded updates implemented
Color = _state ? on_color : Brushes.LightGray;
});
}
}
private List<DigitalInputData> _inputs = new List<DigitalInputData>();
public ICollectionView AvailableDigitalInputs { get; set; }
public MainWindow()
{
InitializeComponent();
// For this example only, set DataContext in this way
DataContext = this;
for( int i=0; i<4; i++) {
_inputs.Add( new DigitalInputData( i, Brushes.Green));
}
AvailableDigitalInputs = CollectionViewSource.GetDefaultView( _inputs);
}
}
}
When I run this application, everything renders properly and according to my databound properties. The click handler works as well, and toggles the state of the LED.
But when I click the LED button numerous times, at some point (maybe after 20 clicks or so), it stops calling my databound ICommand. Why?
I got lucky and figured out a solution to the "freezing" problem, although I do not understand the technical reasoning.
In my DigitalInputData constructor, I created the handler for the RelayCommand using a lambda function instead of passing a reference to a handler. Once I switched over to passing the handler to the RelayCommand constructor, it worked great.
Related
Hi I this two different modes to binding visibility from a parent's property .
for standar control is possible tom use this:
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
</Window.Resources>
<Button x:Name="buttonTest" Width="75" Visibility="{Binding IsModify, Converter={StaticResource BoolToVis}}"/>
but to my usercontrol I only this way to binding the visibility , otherwise doesn't works:
<local:UserControl4 Height="100" Width="100" Visibility="{Binding ElementName=Window1,Path=IsModify,Converter={StaticResource BoolToVis}}"/>
My question is why I need to pass the element name plus ParentProperty at my control visibility , but for button no?
this is the codeBehind
public static readonly DependencyProperty IsModifyProperty =
DependencyProperty.Register("IsModify", typeof(Boolean), typeof(MainWindow), new PropertyMetadata(false));
[DefaultValue(false)]
public Boolean IsModify
{
get { return (Boolean)GetValue(MainWindow.IsModifyProperty); }
set
{
SetValue(MainWindow.IsModifyProperty, value);
this.OnPropertyChanged("IsModify");
}
}
and this is the constructor
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
here the source code to replicate the error.
you needs one project with one window and one usercontrol.
source code for window
---- XAML ----
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:System="clr-namespace:System;assembly=mscorlib" xmlns:local="clr-namespace:XTesting" x:Name="windowTest" x:Class="XTesting.WindowTest"
Title="WindowTest" Height="300" Width="583.908">
<Window.Resources>
<ResourceDictionary>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<local:UserControl4
HorizontalAlignment="Left"
Height="65"
Margin="300,30,0,0"
VerticalAlignment="Top"
Width="230"
Visibility="{Binding IsModify, Converter={StaticResource BoolToVis}}"
Background="#FFF86161" Description="Custom Control"/>
<Button x:Name="buttonTest"
Content="Modify"
HorizontalAlignment="Left"
Margin="45,30,0,0"
VerticalAlignment="Top"
Width="225"
Visibility="{Binding IsModify, Converter={StaticResource BoolToVis}}"
Height="65"/>
<Button x:Name="button"
Content="Skisem (Click me)"
HorizontalAlignment="Left"
Height="75"
Margin="225,160,0,0"
VerticalAlignment="Top"
Width="135"
Click="button_Click_1"
Cursor="Hand"/>
</Grid>
this is the codebehind of window
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace XTesting
{
/// <summary>
/// Interaction logic for WindowTest.xaml
/// </summary>
public partial class WindowTest : Window
{
public static readonly DependencyProperty IsModifyProperty =
DependencyProperty.Register("IsModify", typeof(Boolean), typeof(WindowTest), new PropertyMetadata(false));
[DefaultValue(false)]
public Boolean IsModify
{
get { return (Boolean)GetValue(WindowTest.IsModifyProperty); }
set
{
SetValue(WindowTest.IsModifyProperty, value);
this.OnPropertyChanged("IsModify");
}
}
public WindowTest()
{
InitializeComponent();
this.DataContext = this;
}
private void button_Click_1(object sender, RoutedEventArgs e)
{
IsModify = !IsModify;
}
#region - INotifyPropertyChanged implementation -
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion - INotifyPropertyChanged implementation -
}
}
this is the source code for usercontrol
XAML:
<UserControl x:Class="XTesting.UserControl4"
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"
mc:Ignorable="d"
d:DesignHeight="37" d:DesignWidth="310" MinWidth="7"
MouseLeftButtonUp="Selector1_MouseLeftButtonUp" FontSize="20">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid x:Name="GridRoot">
<Grid.RowDefinitions>
<RowDefinition Height="37*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="51"/>
<ColumnDefinition Width="259*"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="TextBoxDescription"
Margin="0"
TextWrapping="Wrap"
Grid.Column="1"
Text="{Binding Description}"
Background="{x:Null}"
BorderBrush="{x:Null}"
Foreground="{Binding Foreground,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
FontSize="{Binding FontSize,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
VerticalContentAlignment="Center"
IsReadOnly="True"
SelectionBrush="{x:Null}"
BorderThickness="0"
Focusable="False"
IsTabStop="False"
IsUndoEnabled="False"
AllowDrop="False"
Padding="1,0,0,0"
MaxLines="1"
/>
</Grid>
and this is the code behind:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace XTesting
{
/// <summary>
/// Interaction logic for UserControl4.xaml
/// </summary>
public partial class UserControl4 : UserControl, INotifyPropertyChanged
{
#region - Delegate -
public delegate void ClickHandler(Object sender, RoutedEventArgs e);
#endregion - Delegate -
#region - Events -
public event ClickHandler Click;
#endregion - Events -
#region - Dependency Properties mandatory for Binding -
public static readonly DependencyProperty DescriptionProperty =
DependencyProperty.Register("Description", typeof(String), typeof(UserControl4), new PropertyMetadata(String.Empty));
#endregion - Dependency Properties for Binding -
#region - Properties -
/// <summary>
/// Gets or sets the description
/// </summary>
[Category("Common"), Description("gets or sets The description")]
public String Description
{
get { return (String)GetValue(UserControl4.DescriptionProperty); }
set
{
SetValue(UserControl4.DescriptionProperty, value);
this.OnPropertyChanged("Description");
}
}
#endregion - Properties -
public UserControl4()
{
InitializeComponent();
this.DataContext = this;
}
private void Selector1_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (Click != null) Click(sender, e);
}
#region - INotifyPropertyChanged implementation -
// Basically, the UI thread subscribes to this event and update the binding if the received Property Name correspond to the Binding Path element
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion - INotifyPropertyChanged implementation -
}
}
I think the problem is in the constructor This.datacontext= this;
but this is mandatory if i want binding the property description.
Thanks in advance
You should probably add the user control DataContext on the GridRoot element instead of the UserControl4 class itself. The user control usage should be controlled mainly by the instantiating code and only its content should be controlled by the user control itself.
Also, I prefer to assign the data context on the Loaded event instead of the constructor, but this might just be my personal preference.
<UserControl x:Class="XTesting.UserControl4" Loaded="UserControl4_Loaded" ...
// ...
private void UserControl4_Loaded(object sender, RoutedEventArgs e)
{
GridRoot.DataContext = this;
}
I think I am overthinking this at this point, but I have a unique situation.
Using a treeviewI am displaying a table and the fields in that table:
XAML:
<TreeView x:Name="myTreeView" PreviewMouseDoubleClick="myTreeView_MouseLeftButtonDown" SelectedValuePath="Name" ItemsSource="{Binding Fields}" ItemContainerStyle="{StaticResource TreeViewItemExpandedStyle}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Fields}">
<StackPanel Orientation="Horizontal">
<TextBlock Foreground="Black" Text="{Binding Table}" />
</StackPanel>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button BorderBrush="Transparent"
Background="White"
Command="">
<StackPanel>
<Path Margin="5"
Data="M0,5 H10 M5,5 V10Z"
Stroke="#2283B4"
StrokeThickness="1"
Height="10"
Width="10" />
</StackPanel>
</Button>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" />
<TextBlock Height="0" Width="0" Visibility="Collapsed" Text="{Binding Table}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
<!--<HierarchicalDataTemplate.Triggers>
<DataTrigger Binding="{Binding IsExpanded}" Value="True">
<Setter TargetName="treeIcon"
Property="Data"
Value="M0,5 H10"/>
</DataTrigger>
</HierarchicalDataTemplate.Triggers>-->
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Example:
These treeviews are also their own user controls. The issue I am having is I need to be able to use the selected treeview item in another viewmodel. I also have found no other way to grab the selected treeview item and parent except through the code behind. Below is my entire code behind section for this control.
SourceRowUserControl.xaml.cs:
using Alliance.FromAnywhereControl.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Alliance.FromAnywhereControl.ViewModels;
using System.Reflection;
namespace Alliance.FromAnywhereControl
{
/// <summary>
/// Interaction logic for SourceRowUserControl.xaml
/// </summary>
public partial class SourceRowUserControl : UserControl
{
public SourceRowUserControl()
{
InitializeComponent();
}
private ObservableCollection<ConversionRowUserControl> ConversionTypes = new ObservableCollection<ConversionRowUserControl>();
public ObservableCollection<TableInformation> Fields
{
get { return (ObservableCollection<TableInformation>)GetValue(FieldsProperty); }
set { SetValue(FieldsProperty, value); }
}
// Using a DependencyProperty as the backing store for Fields. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FieldsProperty =
DependencyProperty.Register("Fields", typeof(ObservableCollection<TableInformation>), typeof(SourceRowUserControl), new PropertyMetadata(TableInformation.GetAll(null, null, null, null)));
public String FileTypeImage
{
get { return (String)GetValue(FileTypeImageProperty); }
set { SetValue(FileTypeImageProperty, value); }
}
// Using a DependencyProperty as the backing store for FieldTypeImage. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FileTypeImageProperty =
DependencyProperty.Register("FileTypeImage", typeof(String), typeof(SourceRowUserControl), new PropertyMetadata("File Type Image"));
public SolidColorBrush SpacerColor
{
get { return (SolidColorBrush)GetValue(SpacerColorProperty); }
set { SetValue(SpacerColorProperty, value); }
}
// Using a DependencyProperty as the backing store for SpacerColor. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SpacerColorProperty =
DependencyProperty.Register("SpacerColor", typeof(SolidColorBrush), typeof(SourceRowUserControl), new PropertyMetadata(new SolidColorBrush(Colors.Red)));
public String MiddleLabel
{
get { return (String)GetValue(MiddleLabelProperty); }
set { SetValue(MiddleLabelProperty, value); }
}
// Using a DependencyProperty as the backing store for MiddleLabel. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MiddleLabelProperty =
DependencyProperty.Register("MiddleLabel", typeof(String), typeof(SourceRowUserControl), new PropertyMetadata("Middle Label"));
private void myTreeView_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
try
{
Field selectedField = new Field();
selectedField = (Field)myTreeView.SelectedItem;
ConversionViewModel cvm = new ConversionViewModel();
cvm.AddConversionType(selectedField.Table, selectedField.Name);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
The myTreeViewLeftButtonDown event is where is I am grabbing the selected field and table. As you see, I am then calling a method in another viewmodel, but this will not work how I want it to since it creates a new instance. I just put that there to basically show what I was wanting to do. Overview, I need to be able to use a field in my code behind in a viewmodel that is not connected to the view.
Please let me know if you need anymore information or would like to see more code. Thanks in advance!
Looking at your code, instead of defining new ConversionViewModel(), grab the instance of the ConversionViewModel and then set property on it.
public class ConversionViewModel()
{
private static ConversionViewModel _this;
public ConversionViewModel()
{
InitializeComponent();
_this = this;
}
public static ConversionViewModel GetInstance()
{
return _this;
}
//Other prop and methods
}
private void myTreeView_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
try
{
Field selectedField = new Field();
selectedField = (Field)myTreeView.SelectedItem;
ConversionViewModel.GetInstance().AddConversionType(selectedField.Table, selectedField.Name);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
You can give your ItemTemplate an InputBinding to the Left Button Click action.
<DataTemplate>
<StackPanel Orientation="Horizontal">
<StackPanel.InputBindings>
<MouseBinding MouseAction="LeftClick"
Command="{Binding DataContext.SomeViewModelCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=TreeView}}"
CommandParameter="{Binding}"/>
</StackPanel.InputBindings>
...
</StackPanel>
</DataTemplate>
To help explain this, the MouseBinding specifies what command to execute when the LeftClick occurs on the StackPanel.
The Command is bound to the DataContext.SomeViewModelCommand from your TreeView.
The CommandParameter is the Field bound to the DataTemplate.
Now, all you need to do is declare a Command in your View Model (SomeViewModelCommand) where the Command Parameter will be your Field.
I really like the effect that can be seen for example in iOS, which basicaly looks like a layer drawn on top of current view , bluring the visual content and using that as a background. Is there a way to achieve something like that in WPF?
I've seen people mostly dealing with this blur/transparency on Window level, but I need it within the window.
Let's say this is the content of my window.
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
<Image Source="pack://application:,,,/Resources/Penguins.jpg"/>
<Image Source="pack://application:,,,/Resources/Penguins.jpg"/>
</StackPanel>
Which looks like
And now I'd like to draw something on top of that which ( instead of using red background ) blures whatever is beneath it and uses it as background, keeping it's content not blury.
<DockPanel Margin="15" Background="Red">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center">
<Label Content="Some label"/>
<TextBox Width="100" Height="20"/>
</StackPanel>
</DockPanel>
Result:
We will use layering in a Grid. Background: Your main application content. Foreground: Your pseudo-dialog that will have a blurred background.
We will put the background in a border and refer to this border by its name. This will be used in a VisualBrush and provide our to-be-blurred image.
The foreground will also be a layered grid. Background: A rectangle, filled with the brush and using a blur effect. Foreground: Whatever you want to be in front.
Add a reference to System.Windows.Interactivity.
Add the following behavior code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Interactivity;
using System.Windows.Media;
using System.Windows.Media.Effects;
using System.Windows.Shapes;
namespace WpfApplication1
{
public class BlurBackgroundBehavior : Behavior<Shape>
{
public static readonly DependencyProperty BlurContainerProperty
= DependencyProperty.Register(
"BlurContainer",
typeof (UIElement),
typeof (BlurBackgroundBehavior),
new PropertyMetadata(OnContainerChanged));
private static readonly DependencyProperty BrushProperty
= DependencyProperty.Register(
"Brush",
typeof (VisualBrush),
typeof (BlurBackgroundBehavior),
new PropertyMetadata());
private VisualBrush Brush
{
get { return (VisualBrush) this.GetValue(BrushProperty); }
set { this.SetValue(BrushProperty, value); }
}
public UIElement BlurContainer
{
get { return (UIElement) this.GetValue(BlurContainerProperty); }
set { this.SetValue(BlurContainerProperty, value); }
}
protected override void OnAttached()
{
this.AssociatedObject.Effect = new BlurEffect
{
Radius = 80,
KernelType = KernelType.Gaussian,
RenderingBias = RenderingBias.Quality
};
this.AssociatedObject.SetBinding(Shape.FillProperty,
new Binding
{
Source = this,
Path = new PropertyPath(BrushProperty)
});
this.AssociatedObject.LayoutUpdated += (sender, args) => this.UpdateBounds();
this.UpdateBounds();
}
protected override void OnDetaching()
{
BindingOperations.ClearBinding(this.AssociatedObject, Border.BackgroundProperty);
}
private static void OnContainerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((BlurBackgroundBehavior) d).OnContainerChanged((UIElement) e.OldValue, (UIElement) e.NewValue);
}
private void OnContainerChanged(UIElement oldValue, UIElement newValue)
{
if (oldValue != null)
{
oldValue.LayoutUpdated -= this.OnContainerLayoutUpdated;
}
if (newValue != null)
{
this.Brush = new VisualBrush(newValue)
{
ViewboxUnits = BrushMappingMode.Absolute
};
newValue.LayoutUpdated += this.OnContainerLayoutUpdated;
this.UpdateBounds();
}
else
{
this.Brush = null;
}
}
private void OnContainerLayoutUpdated(object sender, EventArgs eventArgs)
{
this.UpdateBounds();
}
private void UpdateBounds()
{
if (this.AssociatedObject != null && this.BlurContainer != null && this.Brush != null)
{
Point difference = this.AssociatedObject.TranslatePoint(new Point(), this.BlurContainer);
this.Brush.Viewbox = new Rect(difference, this.AssociatedObject.RenderSize);
}
}
}
}
Use it in XAML like this:
<Grid>
<Border x:Name="content">
<Border.Background>
<ImageBrush ImageSource="bild1.jpg" />
</Border.Background>
<StackPanel>
<TextBox Width="200" Margin="10" />
<TextBox Width="200" Margin="10" />
<TextBox Width="200" Margin="10" />
</StackPanel>
</Border>
<Grid Margin="59,63,46,110">
<Rectangle ClipToBounds="True">
<i:Interaction.Behaviors>
<wpfApplication1:BlurBackgroundBehavior BlurContainer="{Binding ElementName=content}" />
</i:Interaction.Behaviors>
</Rectangle>
<TextBox VerticalAlignment="Center" Text="Blah" Width="200" Height="30" />
</Grid>
</Grid>
I am trying to set the AutomationProperties.Name property for controls in a GroupStyle control template and it seems to produce nothing. I have it set on the Expander in my template but it says nothing even when I just put in some text without binding. I also tried putting a setter on the GroupItem and that also didn't work. I'm at a bit of a loss. I was hoping the property on the group item would solve it.
XAML:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="WpfApplication8.MainWindow"
x:Name="win"
Title="MainWindow"
Width="640"
Height="480">
<Grid x:Name="LayoutRoot">
<ListBox x:Name="lstbx"
Margin="71,45,99,78"
ItemsSource="{Binding ElementName=win,
Path=Samples}">
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="AutomationProperties.Name"
Value="this is a test" />
<Setter Property="KeyboardNavigation.TabNavigation"
Value="Cycle" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander Name="templateLstBxExpander"
AutomationProperties.Name="test test test"
IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<Label Name="templateLstBxExpanderHeader"
Content="{Binding Path=Name}"
FontWeight="Bold" />
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListBox.GroupStyle>
</ListBox>
</Grid>
</Window>
XAML.cs:
using System;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace WpfApplication8
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public static readonly DependencyProperty sampleProperty = DependencyProperty.Register(
"Samples", typeof(ObservableCollection<sample>), typeof(MainWindow), new PropertyMetadata(new ObservableCollection<sample>()));
public ObservableCollection<sample> Samples
{
get
{
return (ObservableCollection<sample>)this.GetValue(sampleProperty);
}
set
{
this.SetValue(sampleProperty, value);
}
}
public MainWindow()
{
this.InitializeComponent();
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(lstbx.ItemsSource);
PropertyGroupDescription groupDescription = new PropertyGroupDescription("Location");
view.GroupDescriptions.Add(groupDescription);
sample test = new sample();
test.Location = "one";
test.Name = "blah blah";
Samples.Add(test);
sample test2 = new sample();
test2.Location = "two";
test2.Name = "ya ya";
Samples.Add(test2);
}
}
}
sample.cs:
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace WpfApplication8
{
public class sample
{
public string Name { set; get; }
public string Location{ set; get; }
}
The problem is really about how the screenreader is supposed to reach the value. Groupitems are accessible from cursor via MSAA, but not via UIA. UIA is the primary API to use for accessibility in WPF.
The core problem with UIA and WPF, is when trying to read controls which are found by cursor (other ways are focus and caret), usually returns the main window instead.
As a developer of a screenreader myself, the best way to deal with this is to use both MSAA and UIA. If UIA returns nothing of value, fall back to using MSAA.
Try setting AutomationProperties.HelpText alongside with Name.
You can use DisplayMemberPath="Name":
<ListBox x:Name="lstbx" Margin="71,45,99,78" ItemsSource="{Binding ElementName=win, Path=Samples}" DisplayMemberPath="Name" >
Or you can use .ToString():
public class sample
{
public string Name { set; get; }
public string Location { set; get; }
public override string ToString()
{
return Name;
}
}
Can someone tell me why the way I am binding data here does not work? I was able to do this fine with using a GridView. I am not sure why the data is not being passed to the user control here, in addition it compiles fine and shows the user controls with there default text
Main.xaml.cs:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
using Windows.UI.Xaml.Navigation;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
namespace ItemsControlSample
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
/// <summary>
/// Invoked when this page is about to be displayed in a Frame.
/// </summary>
/// <param name="e">Event data that describes how this page was reached. The Parameter
/// property is typically used to configure the page.</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
PopulateData();
}
public class someKindaOfDataHolder
{
public string Text1;
public string Text2;
}
private void PopulateData()
{
List<someKindaOfDataHolder> dataAndStuff = new List<someKindaOfDataHolder>();
for (int i = 0; i < 5; ++i)
{
dataAndStuff.Add(new someKindaOfDataHolder()
{
Text1 = "data spot 1: " + i.ToString(),
Text2 = "data spot 2: " + (i + 2).ToString()
});
}
ListOfAUserControls.ItemsSource = dataAndStuff;
}
}
}
Main.xaml
<Page
x:Class="ItemsControlSample.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ItemsControlSample"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<ScrollViewer>
<ItemsControl x:Name="ListOfAUserControls" ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:CustomUserControl DynamicText1Text="{Binding Text1}" DynamicText2Text="{Binding Text2}" Margin="0,0,10,0"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="100,46,-50,0"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
</Grid>
</Page>
CustomUserControl.xaml.cs:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
// The User Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234236
namespace ItemsControlSample
{
public sealed partial class CustomUserControl : UserControl
{
public static readonly DependencyProperty DynamicText1Property =
DependencyProperty.Register("DynamicText1Text", typeof(string), typeof(CustomUserControl), new PropertyMetadata(null, OnDynamicText1PropertyChanged));
public string DynamicText1Text
{
get { return (string)GetValue(DynamicText1Property); }
set { SetValue(DynamicText1Property, value); }
}
public static readonly DependencyProperty DynamicText2Property =
DependencyProperty.Register("DynamicText2Text", typeof(string), typeof(CustomUserControl), new PropertyMetadata(null, OnDynamicText2PropertyChanged));
public string DynamicText2Text
{
get { return (string)GetValue(DynamicText2Property); }
set { SetValue(DynamicText2Property, value); }
}
public CustomUserControl()
{
this.InitializeComponent();
}
private static void OnDynamicText1PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var obj = d as CustomUserControl;
obj.DynamicText1.Text = e.NewValue.ToString();
}
private static void OnDynamicText2PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var obj = d as CustomUserControl;
obj.DynamicText2.Text = e.NewValue.ToString();
}
}
}
CustomUserControl.xaml:
<UserControl
x:Class="ItemsControlSample.CustomUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ItemsControlSample"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="800"
d:DesignWidth="800">
<Grid Background="Blue">
<Grid.RowDefinitions>
<RowDefinition Height="6*"/>
<RowDefinition Height="2*"/>
</Grid.RowDefinitions>
<Image Source="ms-appx:///Assets/MSIcon.png" />
<StackPanel Grid.Row="1" Margin="25">
<TextBlock Text="Obay" FontSize="45" />
<TextBlock x:Name="DynamicText1" Text="DynamicText1" FontSize="35" />
<TextBlock x:Name="DynamicText2" Text="DynamicText2" FontSize="35" />
</StackPanel>
</Grid>
</UserControl>
Thanks!
You need to set the DataContext somewhere. Without it - your bindings have no context and they fail (look at the Output window in VS when debugging Debug builds to see binding errors). You are setting ItemsSource="{Binding}" where the binding has no context.
Then again - you are overriding the ItemsSource elsewhere with your "ListOfAUserControls.ItemsSource = dataAndStuff;" call, so that is likely not the issue causing your problem in the end. The DataContext of an item in an ItemsControl should be set automatically to an item in the ItemsSource collection.
Another problem that might block you there is that someKindaOfDataHolder.Text1 in your case is a field and it should be a property to work with bindings - try this instead:
public string Text1 { get; set; }