I need to inherit some Editable TextBox properties and add them within the CompoBox properties
For example IsFocused is for reading only
And the SelectionLength is for reading and writing
I made a class inheriting CompoBox
I added a new propertie and tried to change it via the GotFocus
But it doesn't seem to work.
public class MyComboBox : ComboBox
{
TextBox? _textBox;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_textBox = Template.FindName("PART_EditableTextBox", this) as TextBox;
_textBox.GotFocus += _textBox_GotFocus;
_textBox.LostFocus += _textBox_LostFocus;
}
private void _textBox_LostFocus(object sender, RoutedEventArgs e)
{
TextBoxIsFocused = false;
}
private void _textBox_GotFocus(object sender, RoutedEventArgs e)
{
TextBoxIsFocused = true;
}
public static readonly DependencyProperty TextBoxIsFocusedProperty =
DependencyProperty.RegisterAttached(
"TextBoxIsFocused", typeof(bool), typeof(MyComboBox),
new FrameworkPropertyMetadata(
false,
FrameworkPropertyMetadataOptions.AffectsRender));
public static void SetTextBoxIsFocused(ComboBox cb, bool value)
{
cb.SetValue(TextBoxIsFocusedProperty, value);
}
public bool TextBoxIsFocused
{
set => SetTextBoxIsFocused(this, value);
}
}
I don't know how I can update TextBoxIsFocused automatically when the _textBox!. IsFocused value changed
The same goes for writable properties.
How can I set snd get for EditableTextBox properties
Related
I'm trying to create something like expandable button - button with context menu above, which will open by left mouse button click. Only thing I still can't finishing up is items property, which could be setting up in WPF XAML designer like for ContextMenu control. So, as far as I can understand, that means I need to use ItemCollection type for my property. Ok. Let's take a look on my component:
public partial class MenuButton : Button
{
private readonly ContextMenu Menu;
public static readonly DependencyProperty MenuItemsProperty =
DependencyProperty.Register("MenuItems", typeof(ItemCollection), typeof(ContextMenu), new PropertyMetadata(default(ItemCollection), new PropertyChangedCallback(OnSomeMenuItemsPropertyChanged)));
public ItemCollection MenuItems
{
get => (ItemCollection)GetValue(MenuItemsProperty);
set => SetValue(MenuItemsProperty, value);
}
public MenuButton()
{
MenuItems = new DataGrid().Items; // I really need to do it - otherwise I'll get an error
Menu = new ContextMenu
{
HasDropShadow = false,
PlacementTarget = this,
Placement = PlacementMode.Top,
};
InitializeComponent();
}
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
Menu.Width = ActualWidth;
Menu.IsOpen = true;
}
private static void OnSomeMenuItemsPropertyChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
if (target is ContextMenu menu)
{
menu.ItemsSource = (ItemCollection)e.NewValue;
}
}
}
But I can't 'catch' the OnSomeMenuItemsPropertyChanged - breakpoint does not work here. So, that means this mechanism is wrong.
How can I fix that? Should I use OnItemsChanged and OnItemsCollectionChanged events instead (like for ObservableCollection) to handle the changes of the property? Or maybe something else?
So, finally I found the solution. I don't claim that this is the best practice, but maybe it will be useful for someone:
public partial class MenuButton : Button
{
private readonly ContextMenu Menu;
public static readonly DependencyProperty MenuItemsProperty =
DependencyProperty.Register("MenuItems", typeof(ItemCollection), typeof(MenuButton), new PropertyMetadata(default(ItemCollection), new PropertyChangedCallback(OnSomeMenuItemsPropertyChanged)));
public ItemCollection MenuItems
{
get => (ItemCollection)GetValue(MenuItemsProperty);
set => SetValue(MenuItemsProperty, value);
}
public MenuButton()
{
Menu = new ContextMenu
{
HasDropShadow = false,
PlacementTarget = this,
Placement = PlacementMode.Top,
};
MenuItems = new DataGrid().Items;
InitializeComponent();
}
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
Menu.Width = ActualWidth;
Menu.IsOpen = true;
}
private static void OnSomeMenuItemsPropertyChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
var menuButton = target as MenuButton;
menuButton.Menu.ItemsSource = (ItemCollection)e.NewValue;
}
}
I've recently discovered that you should bind passwords in WPF. I found my answer to my problem for my login window, but I'm having a hard time using my helper class to solve the binding problem: PasswordHelper. This class is in one of my project's folder called Utils.
Here's part of my XAML code:
<Window x:Class="IPdevices.LoginWindow"
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:IPdevices"
xmlns:utils="clr-namespace:IPdevices.Utils;assembly=Utils"
mc:Ignorable="d"
Title="IPDevices" Height="451" Width="397.5" Icon="logo3_q2J_icon.ico">
notice the xmlns:utils="clr-namespace:IPdevices.Utils;assembly=Utils"
My password box is now
<PasswordBox utils:PasswordHelper.BindPassword="true" utils:PasswordHelper.BoundPassword="{Binding Path=NetworkPassword, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
However, this doesn't compile because it's screaming:
"The attachable property 'BindPassword' was not found in type 'PasswordHelper'."
It's also screaming:
"The name 'PasswordHelper' does not exist in the namespace 'clr-namespace;IPdevices.Uitls;assembly=Utils'"
And here's this PasswordHelper class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
namespace IPdevices.Utils
{
public static class PasswordHelper
{
public static readonly DependencyProperty BoundPassword =
DependencyProperty.RegisterAttached("BoundPassword", typeof(string), typeof(PasswordHelper), new PropertyMetadata(string.Empty, OnBoundPasswordChanged));
public static readonly DependencyProperty BindPassword = DependencyProperty.RegisterAttached(
"BindPassword", typeof(bool), typeof(PasswordHelper), new PropertyMetadata(false, OnBindPasswordChanged));
private static readonly DependencyProperty UpdatingPassword =
DependencyProperty.RegisterAttached("UpdatingPassword", typeof(bool), typeof(PasswordHelper), new PropertyMetadata(false));
private static void OnBoundPasswordChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
PasswordBox box = d as PasswordBox;
// only handle this event when the property is attached to a PasswordBox
// and when the BindPassword attached property has been set to true
if (d == null || !GetBindPassword(d))
{
return;
}
// avoid recursive updating by ignoring the box's changed event
box.PasswordChanged -= HandlePasswordChanged;
string newPassword = (string)e.NewValue;
if (!GetUpdatingPassword(box))
{
box.Password = newPassword;
}
box.PasswordChanged += HandlePasswordChanged;
}
private static void OnBindPasswordChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
{
// when the BindPassword attached property is set on a PasswordBox,
// start listening to its PasswordChanged event
PasswordBox box = dp as PasswordBox;
if (box == null)
{
return;
}
bool wasBound = (bool)(e.OldValue);
bool needToBind = (bool)(e.NewValue);
if (wasBound)
{
box.PasswordChanged -= HandlePasswordChanged;
}
if (needToBind)
{
box.PasswordChanged += HandlePasswordChanged;
}
}
private static void HandlePasswordChanged(object sender, RoutedEventArgs e)
{
PasswordBox box = sender as PasswordBox;
// set a flag to indicate that we're updating the password
SetUpdatingPassword(box, true);
// push the new password into the BoundPassword property
SetBoundPassword(box, box.Password);
SetUpdatingPassword(box, false);
}
public static void SetBindPassword(DependencyObject dp, bool value)
{
dp.SetValue(BindPassword, value);
}
public static bool GetBindPassword(DependencyObject dp)
{
return (bool)dp.GetValue(BindPassword);
}
public static string GetBoundPassword(DependencyObject dp)
{
return (string)dp.GetValue(BoundPassword);
}
public static void SetBoundPassword(DependencyObject dp, string value)
{
dp.SetValue(BoundPassword, value);
}
private static bool GetUpdatingPassword(DependencyObject dp)
{
return (bool)dp.GetValue(UpdatingPassword);
}
private static void SetUpdatingPassword(DependencyObject dp, bool value)
{
dp.SetValue(UpdatingPassword, value);
}
}
}
I've tried cleaning, rebuilding, double-checking everything but simply won't compile. Any insight?
This class is in one of my project's folder called Utils.
If that means that the class is in the same project as the WPF application that contains the XAML, then that implies that your xmlns is probably wrong.
For the current project it should be:
xmlns:utils="clr-namespace:IPdevices.Utils;assembly="
or
xmlns:utils="clr-namespace:IPdevices.Utils"
The code can be little more simpler and readable as below
Below is the Attached property class
public static class PasswordHelper
{
public static readonly DependencyProperty PasswordProperty =
DependencyProperty.RegisterAttached("Password",
typeof(string), typeof(PasswordHelper),
new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnPasswordPropertyChanged));
public static readonly DependencyProperty AttachProperty =
DependencyProperty.RegisterAttached("Attach",
typeof(bool), typeof(PasswordHelper), new PropertyMetadata(false, Attach));
public static void SetAttach(DependencyObject dp, bool value)
{
dp.SetValue(AttachProperty, value);
}
public static bool GetAttach(DependencyObject dp)
{
return (bool)dp.GetValue(AttachProperty);
}
public static string GetPassword(DependencyObject dp)
{
return (string)dp.GetValue(PasswordProperty);
}
public static void SetPassword(DependencyObject dp, string value)
{
dp.SetValue(PasswordProperty, value);
}
private static void OnPasswordPropertyChanged(DependencyObject sender,
DependencyPropertyChangedEventArgs e)
{
PasswordBox passwordBox = sender as PasswordBox;
if (e.OldValue != e.NewValue)
{
if (!passwordBox.Password.Equals(e.NewValue))
{
passwordBox.Password = (string)e.NewValue;
}
}
}
private static void Attach(DependencyObject sender,
DependencyPropertyChangedEventArgs e)
{
PasswordBox passwordBox = sender as PasswordBox;
if (passwordBox == null)
return;
if ((bool)e.OldValue)
{
passwordBox.PasswordChanged -= PasswordChanged;
}
if ((bool)e.NewValue)
{
passwordBox.PasswordChanged += PasswordChanged;
}
}
private static void PasswordChanged(object sender, RoutedEventArgs e)
{
PasswordBox passwordBox = sender as PasswordBox;
SetPassword(passwordBox, passwordBox.Password);
}
}
Below is the XAML
<PasswordBox local:PasswordAttached.Attach="true" local:PasswordAttached.Password="{Binding Password}" Width="300" Margin="10" Height="26"/>
local is namespace of your PasswordHelper class
Below is the view Model
public class ViewModel : INotifyPropertyChanged
{
private string password;
public string Password
{
get
{
return password;
}
set
{
password = value;
if(PropertyChanged!=null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Password"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
I have a question, someone could help transform this code (used in codebehind ) for use by dependyProperty ?
This code gives the focus to the first item of listview . THX!!!!!!
private void ItemContainerGeneratorOnStatusChanged(object sender, EventArgs eventArgs)
{
if (lvResultado.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
var index = lvResultado.SelectedIndex;
if (index >= 0)
{
var item = lvResultado.ItemContainerGenerator.ContainerFromIndex(index) as ListViewItem;
if (item != null)
{
item.Focus();
}
}
}
}
Specifically, I want to write something like: local:FocusFirstElement.Focus="True" in my XAML instead of writing this code for every list view.
What you are is really an attached behavior, which is implemented via an attached property which is really a special dependency property (which you seem to have hit upon already).
First, create an attached property. This is most easliy accomplished using the propa snippet:
public static bool GetFocusFirst(ListView obj)
{
return (bool)obj.GetValue(FocusFirstProperty);
}
public static void SetFocusFirst(ListView obj, bool value)
{
obj.SetValue(FocusFirstProperty, value);
}
public static readonly DependencyProperty FocusFirstProperty =
DependencyProperty.RegisterAttached("FocusFirst", typeof(bool),
typeof(ListViewExtension), new PropertyMetadata(false));
I'm assuming this is in a static class called ListViewExtenstion. Then, handle the property changed event:
public static readonly DependencyProperty FocusFirstProperty =
DependencyProperty.RegisterAttached("FocusFirst", typeof(bool),
typeof(ListViewExtension), new PropertyMetadata(false, HandleFocusFirstChanged));
static void HandleFocusFirstChanged(
DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
}
In that handler you would check the current value (in e) and register or deregister for the appropriate event on the ListView contained in depObj. Then you would use your existing code to set the focus. Something like:
static void HandleFocusFirstChanged(
DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
ListView lv = (ListView)debObj;
if ((bool)e.NewValue)
lv.StatusChanged += MyLogicMethod;
}
I have created a custom TextEditor control that inherits from AvalonEdit. I have done this to facilitate the use of MVVM and Caliburn Micro using this editor control. The [cut down for display purposes] MvvTextEditor class is
public class MvvmTextEditor : TextEditor, INotifyPropertyChanged
{
public MvvmTextEditor()
{
TextArea.SelectionChanged += TextArea_SelectionChanged;
}
void TextArea_SelectionChanged(object sender, EventArgs e)
{
this.SelectionStart = SelectionStart;
this.SelectionLength = SelectionLength;
}
public static readonly DependencyProperty SelectionLengthProperty =
DependencyProperty.Register("SelectionLength", typeof(int), typeof(MvvmTextEditor),
new PropertyMetadata((obj, args) =>
{
MvvmTextEditor target = (MvvmTextEditor)obj;
target.SelectionLength = (int)args.NewValue;
}));
public new int SelectionLength
{
get { return base.SelectionLength; }
set { SetValue(SelectionLengthProperty, value); }
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged([CallerMemberName] string caller = null)
{
var handler = PropertyChanged;
if (handler != null)
PropertyChanged(this, new PropertyChangedEventArgs(caller));
}
}
Now, in the view that holds this control, I have the following XAML:
<Controls:MvvmTextEditor
Caliburn:Message.Attach="[Event TextChanged] = [Action DocumentChanged()]"
TextLocation="{Binding TextLocation, Mode=TwoWay}"
SyntaxHighlighting="{Binding HighlightingDefinition}"
SelectionLength="{Binding SelectionLength,
Mode=TwoWay,
NotifyOnSourceUpdated=True,
NotifyOnTargetUpdated=True}"
Document="{Binding Document, Mode=TwoWay}"/>
My issue is SelectionLength (and SelectionStart but let us just consider the length for now as the problem is the same). If I selected something with the mouse, the binding from the View to my View Model works great. Now, I have written a find and replace utility and I want to set the SelectionLength (which has get and set available in the TextEditor control) from the code behind. In my View Model I am simply setting SelectionLength = 50, I implement this in the View Model like
private int selectionLength;
public int SelectionLength
{
get { return selectionLength; }
set
{
if (selectionLength == value)
return;
selectionLength = value;
Console.WriteLine(String.Format("Selection Length = {0}", selectionLength));
NotifyOfPropertyChange(() => SelectionLength);
}
}
when I set SelectionLength = 50, the DependencyProperty SelectionLengthProperty does not get updated in the MvvmTextEditor class, it is like the TwoWay binding to my control is failing but using Snoop there is no sign of this. I thought this would just work via the binding, but this does not seem to be the case.
Is there something simple I am missing, or will I have to set up and event handler in the MvvmTextEditor class which listens for changes in my View Model and updated the DP itself [which presents it's own problems]?
Thanks for your time.
This is because the Getter and Setter from a DependencyProperty is only a .NET Wrapper. The Framework will use the GetValue and SetValue itself.
What you can try is to access the PropertyChangedCallback from your DependencyProperty and there set the correct Value.
public int SelectionLength
{
get { return (int)GetValue(SelectionLengthProperty); }
set { SetValue(SelectionLengthProperty, value); }
}
// Using a DependencyProperty as the backing store for SelectionLength. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectionLengthProperty =
DependencyProperty.Register("SelectionLength", typeof(int), typeof(MvvmTextEditor), new PropertyMetadata(0,SelectionLengthPropertyChanged));
private static void SelectionLengthPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var textEditor = obj as MvvmTextEditor;
textEditor.SelectionLength = e.NewValue;
}
Here is another answer if you are still open. Since SelectionLength is already defined as a dependency property on the base class, rather than create a derived class (or add an already existing property to the derived class), I would use an attached property to achieve the same functionality.
The key is to use System.ComponentModel.DependencyPropertyDescriptor to subscribe to the change event of the already existing SelectionLength dependency property and then take your desired action in the event handler.
Sample code below:
public class SomeBehavior
{
public static readonly DependencyProperty IsEnabledProperty
= DependencyProperty.RegisterAttached("IsEnabled",
typeof(bool), typeof(SomeBehavior), new PropertyMetadata(OnIsEnabledChanged));
public static void SetIsEnabled(DependencyObject dpo, bool value)
{
dpo.SetValue(IsEnabledProperty, value);
}
public static bool GetIsEnabled(DependencyObject dpo)
{
return (bool)dpo.GetValue(IsEnabledProperty);
}
private static void OnIsEnabledChanged(DependencyObject dpo, DependencyPropertyChangedEventArgs args)
{
var editor = dpo as TextEditor;
if (editor == null)
return;
var dpDescriptor = System.ComponentModel.DependencyPropertyDescriptor.FromProperty(TextEditor.SelectionLengthProperty,editor.GetType());
dpDescriptor.AddValueChanged(editor, OnSelectionLengthChanged);
}
private static void OnSelectionLengthChanged(object sender, EventArgs e)
{
var editor = (TextEditor)sender;
editor.Select(editor.SelectionStart, editor.SelectionLength);
}
}
Xaml below:
<Controls:TextEditor Behaviors:SomeBehavior.IsEnabled="True">
</Controls:TextEditor>
This is how I did this...
public static readonly DependencyProperty SelectionLengthProperty =
DependencyProperty.Register("SelectionLength", typeof(int), typeof(MvvmTextEditor),
new PropertyMetadata((obj, args) =>
{
MvvmTextEditor target = (MvvmTextEditor)obj;
if (target.SelectionLength != (int)args.NewValue)
{
target.SelectionLength = (int)args.NewValue;
target.Select(target.SelectionStart, (int)args.NewValue);
}
}));
public new int SelectionLength
{
get { return base.SelectionLength; }
//get { return (int)GetValue(SelectionLengthProperty); }
set { SetValue(SelectionLengthProperty, value); }
}
Sorry for any time wasted. I hope this helps someone else...
I created my own DataGrid control which inherits from DataGrid. I declared a Dependency Property which I want to use at column level, so on the PreviewKeyDown event I check the value and decide if this current cell needs to be handled or not.
public class MyDataGrid : DataGrid
{
public static DependencyProperty HandleKeyPressEventProperty =
DependencyProperty.RegisterAttached(
"HandleKeyPressEvent",
typeof(bool),
typeof(MyDataGrid),
new FrameworkPropertyMetadata(true));
public bool HandleKeyPressEvent
{
get { return (bool)GetValue(HandleKeyPressEventProperty); }
set { SetValue(HandleKeyPressEventProperty, value); }
}
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
if (HandleKeyPressEvent)
{
HandleKeyPress(e);
}
else
{
base.OnPreviewKeyDown(e);
}
}
}
My XAML looks like this:
<MyDataGrid x:Name="myDataGrid">
<DataGridTextColumn MyDataGrid.HandleKeyPressEvent = "True" />
<DataGridTemplateColumn MyDataGrid.HandleKeyPressEvent = "False"/>
</MyDataGrid>
But I am having a real problem to have this dependency property available at the column level. What I try to do is just like Grid.Column. Can someone help me with that?
An attached property has a static Get method and a static Set method (which are declared by the property name prefixed by Get/Set) instead of a CLR Property wrapper. To check the current column in OnPreviewKeyDown, you can use CurrentCell.Column
public class MyDataGrid : DataGrid
{
public static readonly DependencyProperty HandleKeyPressEventProperty =
DependencyProperty.RegisterAttached("HandleKeyPressEvent",
typeof(bool),
typeof(MyDataGrid),
new UIPropertyMetadata(true));
public static bool GetHandleKeyPressEvent(DependencyObject obj)
{
return (bool)obj.GetValue(HandleKeyPressEventProperty);
}
public static void SetHandleKeyPressEvent(DependencyObject obj, bool value)
{
obj.SetValue(HandleKeyPressEventProperty, value);
}
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
if (GetHandleKeyPressEvent(CurrentCell.Column) == true)
{
HandleKeyPress(e);
}
else
{
base.OnPreviewKeyDown(e);
}
}
}