WPF: Can't get my control to take focus - c#

I can't seem to get my control to actually take the focus:
XAML:
<Button Command={Binding SetGridToVisibleCommand} />
<Grid Visibility="{Binding IsGridVisible, Converter={con:VisibilityBooleanConverter}}">
<TextBox Text={Binding MyText} IsVisibleChanged="TextBox_IsVisibleChanged" />
</Grid>
XAML.cs:
private void TextBox_IsVisibleChanged(Object sender, DependencyPropertyChangedEventArgs e)
{
UIElement element = sender as UIElement;
if (element != null)
{
Boolean success = element.Focus(); //Always returns false and doesn't take focus.
}
}
The ViewModel does it's job of setting the IsGridVisible to true, and the converter does it's job by converting that value to Visibility.Visible (I snooped it).

Not all UIElements can be focused by default, have you tried setting Focusable to true before trying Focus()?

We use this with in our application:
public static class Initial
{
public static void SetFocus(DependencyObject obj, bool value)
{
obj.SetValue(FocusProperty, value);
}
public static readonly DependencyProperty FocusProperty =
DependencyProperty.RegisterAttached(
"Focus", typeof(bool), typeof(Initial),
new UIPropertyMetadata(false, HandleFocusPropertyChanged));
private static void HandleFocusPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var element = (UIElement)d;
if ((bool)e.NewValue)
element.Focus(); // Ignore false values.
}
}
And the usage is:
<TextBox Text="{Binding FooProperty, UpdateSourceTrigger=PropertyChanged}"
Grid.Column="1" HorizontalAlignment="Stretch"
Style="{StaticResource EditText}" ui:Initial.Focus="True"/>
The original idea came from SO, but couldn't find the answer.
100% free code behind :P
HTH

I couldn't reproduce your problem, its working fine with me, try the following code on a new project as a proof of concept and see if it works with you:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<Button Content="Click" Click="Button_Click" />
<Grid>
<TextBox Name="NameTextBox" Text="ddd"
IsVisibleChanged="TextBox_IsVisibleChanged" />
</Grid>
</StackPanel>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void TextBox_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
UIElement element = sender as UIElement;
if (element != null)
{
Boolean success = element.Focus(); //Always returns false and doesn't take focus.
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if (NameTextBox.IsVisible)
NameTextBox.Visibility = Visibility.Collapsed;
else
NameTextBox.Visibility = Visibility.Visible;
}
}

Related

UserControl in WinUI 3: how to set the 'source' property of an image and the 'click' event of a button?

I'm building a UserControl that should display a button with an image and text.
I access that UserControl in the App like this:
<local:ButtonWithImage
ButtonClick="Button1_Click"
ButtonImage="Assets/Clipboard 4.png"
ButtonText="Clipboard History"
ButtonWidth="200" />
Out of the 4 properties displayed in the code above, two of them are working fine, which are ButtonText and ButtonWidth.
But the ButtonClick and ButtonImage properties are causing errors, which I'll explain next.
The UserControl code is this:
xaml:
<UserControl
x:Class="Launcher_WinUI3_Net_6.ButtonWithImage"
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:local="using:Launcher_WinUI3_Net_6"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel>
<StackPanel Orientation="Horizontal">
<Button x:Name="button">
<StackPanel Orientation="Horizontal">
<Image x:Name="image"/>
<TextBlock x:Name="textBlock" />
</StackPanel>
</Button>
</StackPanel>
<TextBlock Height="1" />
</StackPanel>
</UserControl>
C#:
public sealed partial class ButtonWithImage : UserControl
{
public ButtonWithImage()
{
this.InitializeComponent();
}
public string ButtonText
{
get { return (string)GetValue(ButtonTextProperty); }
set { SetValue(ButtonTextProperty, value); }
}
public static readonly DependencyProperty
ButtonTextProperty =
DependencyProperty.Register("ButtonText",
typeof(string), typeof(ButtonWithImage),
new PropertyMetadata(string.Empty, ButtonTextValue));
private static void ButtonTextValue(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var buttonWithImage = d as ButtonWithImage;
var buttonWithImageProperty = buttonWithImage.FindName("textBlock") as TextBlock;
buttonWithImageProperty.Text = e.NewValue.ToString();
}
public string ButtonWidth
{
get { return (string)GetValue(ButtonWidthProperty); }
set { SetValue(ButtonWidthProperty, value); }
}
public static readonly DependencyProperty
ButtonWidthProperty =
DependencyProperty.Register("ButtonWidth",
typeof(string), typeof(ButtonWithImage),
new PropertyMetadata(string.Empty, ButtonWidthValue));
private static void ButtonWidthValue(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var buttonWithImage = d as ButtonWithImage;
var buttonWithImageProperty = buttonWithImage.FindName("button") as Button;
buttonWithImageProperty.Width = Convert.ToDouble(e.NewValue.ToString());
}
public string ButtonClick
{
get { return (string)GetValue(ButtonClickProperty); }
set { SetValue(ButtonClickProperty, value); }
}
public static readonly DependencyProperty
ButtonClickProperty =
DependencyProperty.Register("ButtonClick",
typeof(string), typeof(ButtonWithImage),
new PropertyMetadata(string.Empty, ButtonClickValue));
private static void ButtonClickValue(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var buttonWithImage = d as ButtonWithImage;
var buttonWithImageProperty = buttonWithImage.FindName("button") as Button;
buttonWithImageProperty.Click += e.NewValue.ToString();
}
public string ButtonImage
{
get { return (string)GetValue(ButtonImageProperty); }
set { SetValue(ButtonImageProperty, value); }
}
public static readonly DependencyProperty
ButtonImageProperty =
DependencyProperty.Register("ButtonImage",
typeof(string), typeof(ButtonWithImage),
new PropertyMetadata(string.Empty, ButtonImageValue));
private static void ButtonImageValue(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var buttonWithImage = d as ButtonWithImage;
var buttonWithImageProperty = buttonWithImage.FindName("image") as Image;
buttonWithImageProperty.Source = e.NewValue.ToString();
}
}
The code for the ButtonClick is generating this error: Cannot implicitly convert type 'string' to 'Microsoft.UI.Xaml.RoutedEventHandler'
And the code for the ButtonImage is generating this error: Cannot implicitly convert type 'string' to 'Microsoft.UI.Xaml.Media.ImageSource'
I don't have much experience with creating UserControls so I'm following some examples I've seen on the internet, but none of them address these two problems I'm facing.
========================================================
Update 1 based on answer from Andrew KeepCoding:
Thanks Andrew!!!
There is still an error going on: No overload for 'Button52_Click' matches delegate 'EventHandler'
UserControl in the App:
<local:ButtonWithImage
ButtonImage="Assets/Clipboard 4.png"
ButtonText="Clipboard History"
ButtonWidth="200"
Click="Button52_Click" />
Button52_Click signature:
private void Button52_Click(object sender, RoutedEventArgs e)
{
foo();
}
UserControl 'Click' event signature:
public event EventHandler? Click;
private void button_Click(object sender, RoutedEventArgs e)
{
Click?.Invoke(this, EventArgs.Empty);
}
The signatures are the same, even so the error No overload for 'Button52_Click' matches delegate 'EventHandler' is occurring
The error is occurring here, in 'case 41:':
case 40: // MainWindow.xaml line 1288
{
global::Microsoft.UI.Xaml.Controls.Button element40 = global::WinRT.CastExtensions.As<global::Microsoft.UI.Xaml.Controls.Button>(target);
((global::Microsoft.UI.Xaml.Controls.Button)element40).Click += this.Button51_Click;
}
break;
case 41: // MainWindow.xaml line 1199
{
global::Launcher_WinUI3_Net_6.ButtonWithImage element41 = global::WinRT.CastExtensions.As<global::Launcher_WinUI3_Net_6.ButtonWithImage>(target);
((global::Launcher_WinUI3_Net_6.ButtonWithImage)element41).Click += this.Button52_Click;
}
break;
========================================================
Update 2:
The Button52_Click signature should be:
private void Button52_Click(object sender, EventArgs e)
{
foo();
}
And not:
private void Button52_Click(object sender, RoutedEventArgs e)
{
foo();
}
Instead of typeof(string), you should use the actual type for your dependency properties.
For example, I'm using ImageSource for the ButtonImage in the code below:
<UserControl
x:Class="UserControls.ButtonWithImage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel>
<StackPanel Orientation="Horizontal">
<Button
x:Name="button"
Click="button_Click">
<StackPanel Orientation="Horizontal">
<Image
x:Name="image"
Source="{x:Bind ButtonImage, Mode=OneWay}" />
<TextBlock
x:Name="textBlock"
Text="{x:Bind ButtonText, Mode=OneWay}" />
</StackPanel>
</Button>
</StackPanel>
<TextBlock Height="1" />
</StackPanel>
</UserControl>
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using System;
namespace UserControls;
public sealed partial class ButtonWithImage : UserControl
{
public static readonly DependencyProperty ButtonTextProperty = DependencyProperty.Register(
nameof(ButtonText),
typeof(string),
typeof(ButtonWithImage),
new PropertyMetadata(default));
public static readonly DependencyProperty ButtonImageProperty = DependencyProperty.Register(
nameof(ButtonImage),
typeof(ImageSource),
typeof(ButtonWithImage),
new PropertyMetadata(default));
public ButtonWithImage()
{
this.InitializeComponent();
}
public event EventHandler? Click;
public string ButtonText
{
get => (string)GetValue(ButtonTextProperty);
set => SetValue(ButtonTextProperty, value);
}
public ImageSource? ButtonImage
{
get => (ImageSource?)GetValue(ButtonImageProperty);
set => SetValue(ButtonImageProperty, value);
}
private void button_Click(object sender, RoutedEventArgs e)
{
Click?.Invoke(this, EventArgs.Empty);
}
}
And use it like this:
<local:ButtonWithImage
ButtonText="Text"
ButtonImage="Assets/StoreLogo.png"
Click="ButtonWithImage_Click" />
private void ButtonWithImage_Click(object sender, EventArgs e)
{
}
You should also consider a custom control derived from a Button. These videos might help.
UserControls
CustomControls

WPF bind point to UserControl

I have a user control that contains 2 DoubleUpDown, I have bound point to that controls
<DoubleUpDown x:Name="X" Grid.Column="1" Grid.Row="0" Value="{Binding Path=Value.X, Mode=TwoWay" />
<DoubleUpDown x:Name="Y" Grid.Column="1" Grid.Row="1" Value="{Binding Path=Value.Y, Mode=TwoWay}" />
controls get updated pretty well when I change Value from outside, but Value stays unchanged when I change controls data.
I bound Value to user control from code inside
Point2DEditorView editor = new Point2DEditorView();
Binding binding = new Binding("Value");
binding.Mode = BindingMode.TwoWay;
editor.SetBinding(Point2DEditorView.ValueProperty, binding);
and Point2DEditorView.Value also changed when I insert new coordinates into controls. But that does not affect bound Value.
Point is a value type data. Because of this when you bind it to control boxing and unboxing occurs. For more information see this. So, you may easy solve this problem by creating your own class (not struct!):
class MyPoint
{
public int X { set; get; }
public int Y { set; get; }
}
And then bind this objects to your control and you will see that all works as you expect.
Update
First of all your DoubleUpDown is'n in standart FCL and I think your problem in it. There is a simple example where all works as expect. I created a simple UpDown control for it:
Point class
public class Point2D : INotifyPropertyChanged
{
private double x;
private double y;
public double X
{
set
{
if (value.Equals(x)) return;
x = value;
OnPropertyChanged();
}
get { return x; }
}
public double Y
{
set
{
if (value.Equals(y)) return;
y = value;
OnPropertyChanged();
}
get { return y; }
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
UpDown xaml
<UserControl x:Name="doubleUpDown" x:Class="PointBind.DoubleUpDown"
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:DesignWidth="105" Height="33">
<StackPanel Orientation="Horizontal" DataContext="{Binding ElementName=doubleUpDown}">
<TextBox Margin="5,5,0,5" Width="50" Text="{Binding Value}" />
<Button x:Name="Up" x:FieldModifier="private" Margin="5,5,0,5" Content="˄" Width="20" Click="Up_Click" />
<Button x:Name="Down" x:FieldModifier="private" Margin="0,5,0,5" Content="˅" Width="20" Click="Down_Click" />
</StackPanel>
</UserControl>
UpDown .cs
public partial class DoubleUpDown : UserControl
{
public double Value
{
get { return (double)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
// Using a DependencyProperty as the backing store for Value. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(double), typeof(DoubleUpDown), new PropertyMetadata(0.0));
public DoubleUpDown()
{
InitializeComponent();
DataContext = this;
}
private void Up_Click(object sender, RoutedEventArgs e)
{
Value++;
}
private void Down_Click(object sender, RoutedEventArgs e)
{
Value--;
}
}
Point2DEditorView xaml
<UserControl x:Name="point2DEditorView" x:Class="PointBind.Point2DEditorView"
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"
xmlns:local="clr-namespace:PointBind"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel>
<local:DoubleUpDown Value="{Binding Point.X, ElementName=point2DEditorView, Mode=TwoWay}"/>
<local:DoubleUpDown Value="{Binding Point.Y, ElementName=point2DEditorView, Mode=TwoWay}"/>
</StackPanel>
</UserControl>
UpDown .cs
public partial class Point2DEditorView : UserControl
{
public Point2D Point
{
get { return (Point2D)GetValue(PointProperty); }
set { SetValue(PointProperty, value); }
}
// Using a DependencyProperty as the backing store for Point. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PointProperty =
DependencyProperty.Register("Point", typeof (Point2D), typeof (Point2DEditorView),
new PropertyMetadata(new Point2D {X = 10, Y = 20}));
public Point2DEditorView()
{
InitializeComponent();
}
}
Test form xaml
<Window x:Class="PointBind.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PointBind"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:Point2DEditorView x:Name="pointEditor"/>
<Button Content="Button" HorizontalAlignment="Left" Margin="39,121,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
</Grid>
</Window>
And test form .cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
pointEditor.Point = new Point2D{X = 300, Y = 400};
}
}
Hope this helps.

TextBox in UserControl, can't edit Text dependency property from code behind

My UserControl contains a TextBox and a Button. The TextBox's Text is correctly populated by a dependency property called X.
My Goal:
Change the value of X (e.g. Text of the TextBox) when I press the Button.
I have defined the UserControl as follows:
<StackPanel Orientation="Horizontal" >
<TextBox Name="Xbox" Text="{Binding Path=X}" Width="50"/>
<Button Content="Current" Click="InsertCurrentBtnClick" />
</StackPanel>
With codebehind:
public double X
{
get { return (double)GetValue(XProperty); }
set { SetValue(XProperty, value); }
}
public static readonly DependencyProperty XProperty =
DependencyProperty.Register("X", typeof(double), typeof(MyUserControl), new PropertyMetadata(0.0));
private void InsertCurrentBtnClick(object sender, RoutedEventArgs e)
{
X = 0.7;
//BindingOperations.GetBindingExpression(this, XProperty).UpdateTarget();
//BindingOperations.GetBindingExpression(Xbox, TextBox.TextProperty).UpdateTarget();
//BindingOperations.GetBindingExpression(Xbox, XProperty).UpdateTarget();
//Xbox.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
//GetBindingExpression(XProperty).UpdateTarget();
}
I have tried several things - one at a time - (see below X=0.7;) to force the update to the TextBox Text but nothing has helped so far.
Thanks in advance.
I'd write it in this way:
public double X
{
get { return (double)GetValue(XProperty); }
set { SetValue(XProperty, value); }
}
public static readonly DependencyProperty XProperty =
DependencyProperty.Register("X", typeof(double), typeof(MainPage), new PropertyMetadata(new PropertyChangedCallback(Callback)));
public static void Callback(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
(o as MainPage).Xbox.Text = e.NewValue.ToString();
}
private void InsertCurrentBtnClick(object sender, RoutedEventArgs e)
{
X = 0.7;
}
And the xaml code:
<StackPanel Orientation="Horizontal" >
<TextBox Name="Xbox" Width="50"/>
<Button Content="Current" Click="InsertCurrentBtnClick" />
</StackPanel>
You need to set the DataContext for you Control. As I see X defined in your control, you need to do this :
public MyUserControl()
{
InitializeComponent();
// add this line
this.DataContext = this;
}
Although, you can bind it as well, just change the xaml:
<UserControl x:Class="SilverlightApplication1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Name="myWidnow"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<StackPanel Orientation="Horizontal" >
<TextBox Name="Xbox" Width="50" Text="{Binding ElementName=myWidnow, Path=X}" />
<Button Content="Current" Click="InsertCurrentBtnClick" />
</StackPanel>
</Grid>
Notice that I've added the Name proeprty to the UserControl.
In this case, you don't have to change anything in the code behid.

showing password characters on some event for passwordbox

I am developing a windows phone application.In that i ask the user to login.
On the login page the user has to enter password.
Now what I want is that i give user a check box which when selected should show the characters of the password.
I have not seen any property on password box to show password characters.
Please suggest some way to do it.
Don't think that is possible with PasswordBox... just a thought, but you might accomplish the same result using a hidden TextBox and when the user clicks the CheckBox, you just hide the PasswordBox and show the TextBox; if he clicks again, you switch their Visibility state again, and so on...
Edit
And here it is how!
Just add a page, change the ContentPanel to a StackPanel and add this XAML code:
<PasswordBox x:Name="MyPasswordBox" Password="{Binding Text, Mode=TwoWay, ElementName=MyTextBox}"/>
<TextBox x:Name="MyTextBox" Text="{Binding Password, Mode=TwoWay, ElementName=MyPasswordBox}" Visibility="Collapsed" />
<CheckBox x:Name="ShowPasswordCharsCheckBox" Content="Show password" Checked="ShowPasswordCharsCheckBox_Checked" Unchecked="ShowPasswordCharsCheckBox_Unchecked" />
Next, on the page code, add the following:
private void ShowPasswordCharsCheckBox_Checked(object sender, RoutedEventArgs e)
{
MyPasswordBox.Visibility = System.Windows.Visibility.Collapsed;
MyTextBox.Visibility = System.Windows.Visibility.Visible;
MyTextBox.Focus();
}
private void ShowPasswordCharsCheckBox_Unchecked(object sender, RoutedEventArgs e)
{
MyPasswordBox.Visibility = System.Windows.Visibility.Visible;
MyTextBox.Visibility = System.Windows.Visibility.Collapsed;
MyPasswordBox.Focus();
}
This works fine, but with a few more work, you can do this fully MVVM'ed!
with default passwordbox it's not possible to implement the feature you want.
more information you can find here: http://social.msdn.microsoft.com/Forums/en/wpf/thread/98d0d4d4-1463-481f-b8b1-711119a6ba99
I created an MVVM example, which I'm also using in real life. Please note that PasswordBox.Password is not a Dependency Property and thus cannot be bound directly. It's designed like that for security reasons, for details see: How to bind to a PasswordBox in MVVM
If you want to do it anyway, you have to build a bridge to your view model using code behind. I'm not providing the converters, as you're probably using your own set of converters. If not, please ask google for suitable implementations.
EnterPasswordWindow.xaml
<Window x:Class="MyDemoApp.Controls.EnterPasswordWindow"
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:MyDemoApp.Controls"
mc:Ignorable="d" d:DataContext="{d:DesignInstance local:EnterPasswordViewModel}"
WindowStartupLocation="CenterOwner" ResizeMode="NoResize" SizeToContent="WidthAndHeight"
Title="Enter Password">
<StackPanel Margin="4">
<TextBlock Margin="4">Please enter a password:</TextBlock>
<TextBox Margin="4" Text="{Binding Password, UpdateSourceTrigger=PropertyChanged}" Visibility="{Binding ShowPassword, Converter={StaticResource BoolToVisibleConverter}}"/>
<PasswordBox Margin="4" Name="PasswordBox" Visibility="{Binding ShowPassword, Converter={StaticResource BoolToCollapsedConverter}}" PasswordChanged="PasswordBox_PasswordChanged"/>
<CheckBox Margin="4" IsChecked="{Binding ShowPassword}">Show password</CheckBox>
<DockPanel>
<Button Margin="4" Width="150" Height="30" IsDefault="True" IsEnabled="{Binding Password, Converter={StaticResource StringIsNotNullOrEmptyConverter}}" Click="Button_Click">OK</Button>
<Button Margin="4" Width="150" Height="30" IsCancel="True" HorizontalAlignment="Right">Cancel</Button>
</DockPanel>
</StackPanel>
</Window>
EnterPasswordWindow.xaml.cs
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace MyDemoApp.Controls
{
/// <summary>
/// Interaction logic for EnterPasswordWindow.xaml
/// </summary>
public partial class EnterPasswordWindow : Window
{
public EnterPasswordWindow()
{
InitializeComponent();
DataContext = ViewModel = new EnterPasswordViewModel();
ViewModel.PropertyChanged += ViewModel_PropertyChanged;
}
public EnterPasswordViewModel ViewModel { get; set; }
private void Button_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
Close();
}
private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (!mSuppressPropertyChangedEvent && e.PropertyName == nameof(ViewModel.Password))
{
PasswordBox.Password = ViewModel.Password;
}
}
private bool mSuppressPropertyChangedEvent;
private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
mSuppressPropertyChangedEvent = true;
ViewModel.Password = ((PasswordBox)sender).Password;
mSuppressPropertyChangedEvent = false;
}
}
}
EnterPasswordViewModel.cs
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace MyDemoApp.Controls
{
public class EnterPasswordViewModel : INotifyPropertyChanged
{
public string Password
{
get => mPassword;
set
{
if (mPassword != value)
{
mPassword = value;
NotifyPropertyChanged();
}
}
}
private string mPassword;
public bool ShowPassword
{
get => mShowPassword;
set
{
if (mShowPassword != value)
{
mShowPassword = value;
NotifyPropertyChanged();
}
}
}
private bool mShowPassword;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName]string propertyName = null)
{
if (string.IsNullOrEmpty(propertyName))
{
return;
}
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
You could create your own control that inherits from textbox however after each character you replace it with an *, storing the true value within a private variable on the page. Using a checkbox you can then toggle whether the value in the textbox shows the true value or the * value.
It's not an elegant solution nor is it a best practice, however I think it's still an alternative if you are willing to live with it.

Dependency Property in User Control works only on the first instance

I have several custom user controls in a window. They appear dynamically, like workspaces.
I need to add a dependency property on an itemscontrol to trigger a scrolldown when an item is being added to the bound observable collection to my itemscontrol, like so:
(usercontrol)
<ScrollViewer VerticalScrollBarVisibility="Auto" >
<ItemsControl Grid.Row="0" ItemsSource="{Binding Messages}" View:ItemsControlBehavior.ScrollOnNewItem="True">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox IsReadOnly="True" TextWrapping="Wrap" Text="{Binding Path=DataContext, RelativeSource={RelativeSource Self}}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
And the code of my dependency property :
public class ItemsControlBehavior
{
static readonly Dictionary<ItemsControl, Capture> Associations =
new Dictionary<ItemsControl, Capture>();
public static bool GetScrollOnNewItem(DependencyObject obj)
{
return (bool)obj.GetValue(ScrollOnNewItemProperty);
}
public static void SetScrollOnNewItem(DependencyObject obj, bool value)
{
obj.SetValue(ScrollOnNewItemProperty, value);
}
public static readonly DependencyProperty ScrollOnNewItemProperty =
DependencyProperty.RegisterAttached(
"ScrollOnNewItem",
typeof(bool),
typeof(ItemsControl),
new UIPropertyMetadata(false, OnScrollOnNewItemChanged));
public static void OnScrollOnNewItemChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var mycontrol = d as ItemsControl;
if (mycontrol == null) return;
bool newValue = (bool)e.NewValue;
if (newValue)
{
mycontrol.Loaded += new RoutedEventHandler(MyControl_Loaded);
mycontrol.Unloaded += new RoutedEventHandler(MyControl_Unloaded);
}
else
{
mycontrol.Loaded -= MyControl_Loaded;
mycontrol.Unloaded -= MyControl_Unloaded;
if (Associations.ContainsKey(mycontrol))
Associations[mycontrol].Dispose();
}
}
static void MyControl_Unloaded(object sender, RoutedEventArgs e)
{
var mycontrol = (ItemsControl)sender;
Associations[mycontrol].Dispose();
mycontrol.Unloaded -= MyControl_Unloaded;
}
static void MyControl_Loaded(object sender, RoutedEventArgs e)
{
var mycontrol = (ItemsControl)sender;
var incc = mycontrol.Items as INotifyCollectionChanged;
if (incc == null) return;
mycontrol.Loaded -= MyControl_Loaded;
Associations[mycontrol] = new Capture(mycontrol);
}
class Capture : IDisposable
{
public ItemsControl mycontrol{ get; set; }
public INotifyCollectionChanged incc { get; set; }
public Capture(ItemsControl mycontrol)
{
this.mycontrol = mycontrol;
incc = mycontrol.ItemsSource as INotifyCollectionChanged;
incc.CollectionChanged +=incc_CollectionChanged;
}
void incc_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
ScrollViewer sv = mycontrol.Parent as ScrollViewer;
sv.ScrollToBottom();
}
}
public void Dispose()
{
incc.CollectionChanged -= incc_CollectionChanged;
}
}
}
During the first instantiation of my user control, it works like a charm.
But when another user control of the same type is dynamically instantiated, the DependencyProperty is never attached anymore to my scrollviewer. Only the first instance will work correctly.
I know that dependency properties are static, but does that mean they can't work at the same time on several user control of the same type added to the window?
Update 02/03 : Here's how I set the viewmodel to the view (not programmatically) :
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:testDp.ViewModel"
xmlns:View="clr-namespace:testDp.View">
<DataTemplate DataType="{x:Type vm:ChatTabViewModel}">
<View:ChatTabView />
</DataTemplate>
</ResourceDictionary>
even with x:shared = false in the datatemplate tag, it won't work.
But if I set the datacontext in a classic way like usercontrol.datacontext = new viewmodel(), it definitely work. But it's recommended to have a "shared" view, so how do we make dependency properties work with this "xaml" way of setting datacontext ?
Sorry, I couldn't reproduce your problem.
I started Visual C# 2010 Express, created a new 'WPF Application', added your XAML to a UserControl that I imaginatively titled UserControl1, and added your ItemsControlBehavior class. I then modified the MainWindow that VC# created for me as follows:
MainWindow.xaml (contents of <Window> element only):
<StackPanel Orientation="Vertical">
<Button Content="Add user control" Click="ButtonAddUserControl_Click" />
<Button Content="Add message" Click="ButtonAddMessage_Click" />
<StackPanel Orientation="Horizontal" x:Name="sp" Height="300" />
</StackPanel>
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public ObservableCollection<string> Messages { get; private set; }
public MainWindow()
{
InitializeComponent();
Messages = new ObservableCollection<string>() { "1", "2", "3", "4" };
DataContext = this;
}
private void ButtonAddUserControl_Click(object sender, RoutedEventArgs e)
{
sp.Children.Add(new UserControl1());
}
private void ButtonAddMessage_Click(object sender, RoutedEventArgs e)
{
Messages.Add((Messages.Count + 1).ToString());
}
}
I made no modifications to the XAML in your UserControl, nor to your ItemsControlBehavior class.
I found that no matter how many user controls were added, their ScrollViewers all scrolled down to the bottom when I clicked the 'Add message' button.
If you're only seeing the scroll-to-the-bottom behaviour on one of your user controls, then there must be something that you're not telling us.

Categories