Reputation: 3682
I've found this question which helps me achieve part of what I want to do: MVVM- How can I select text in a textbox?
My first problem is that I have two text boxes, with a "Select All" button for each, but I can't work out how to adapt the accepted answer so that I can control each independently.
Also, I would like to add a "Copy selected text" button for each.
How can I do this whilst sticking to the MVVM pattern?
Upvotes: 2
Views: 3576
Reputation: 1994
You can either bind the two buttons to different commands which invoke work on different textboxes, or you could use commandParameters to differentiate which to work on.
You can follow through on your linked post by creating an AttachedProperty or just make a custom control. What you need to do essentially is create a bindable property for the text selection. TextBox's property "SelectedText" sounds like a great idea but if you try to bind to it in WPF it throws an error as it is not a DependencyProperty. A property must be a DependencyProperty for you to bind to it.
So you create one, such as IsTextSelected as a bool, and when it changes, your AttachedProperty or custom control handles it and does SelectAll() or maybe SelectedText=Text;
I suggested AttachedProperty if doing a single item. However, you asked for custom control, which I believe should be used if doing multiple functionality improvements to one type of control, of which they are not to be reused on different type.
public class SmartTextBox : TextBox
{
public static readonly DependencyProperty IsSelectedTextProperty = DependencyProperty.RegisterAttached("IsSelectedText",
typeof(bool), typeof(SmartTextBox), new FrameworkPropertyMetadata(false, OnIsSelectedChanged));
public bool IsSelectedText
{
get { return (bool)GetValue(IsSelectedTextProperty); }
set { SetValue(IsSelectedTextProperty, value); }
}
private static void OnIsSelectedChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
SmartTextBox textbox = sender as SmartTextBox;
if ((bool)e.NewValue)
{
textbox.Focus();
textbox.SelectAll();
}
}
}
Usage: ViewModel
set
so that you can force it at any time, not keeping track of what user has done.Note2 Make multiple properties, IsSelectedUsername, IsSelectedFilepath etc, and bind these. Each SmartTextBox is bound to one and will handle one that change.
public bool IsSelectedText
{
get { return isSelectedText; }
set
{
isSelectedText = value;
RaisePropertyChanged("IsSelectedText");
}
}
private void SelectAllExecute()
{
IsSelectedText = true;
}
Usage: XAML
xmlns:custom="clr-namespace:xyz.View.Controls"
<custom:SmartTextBox Text="{Binding Path=MyText}"
IsSelectedText="{Binding Path=IsSelectedText}"/>
Retrieving the selected text, you need to add to the custom control a new dependency property you can bind to as well as the means for the control to update it. I chose when the control leaves focus rather than selection changed since I expect the user to do something like click a button before I need to know the selected text.
public static readonly DependencyProperty SelectedText2Property = DependencyProperty.RegisterAttached("SelectedText2",
typeof(string), typeof(SmartTextBox), new PropertyMetadata(""));
public string SelectedText2
{
get { return (string)GetValue(SelectedText2Property); }
set { SetValue(SelectedText2Property, value); }
}
protected override void OnLostFocus(RoutedEventArgs e)
{
SelectedText2 = this.SelectedText;
base.OnLostFocus(e);
}
XAML has it bound now:
<custom:SmartTextBox Text="{Binding Path=MyText}"
SelectedText2="{Binding Path=TheSelectedText, Mode=OneWayToSource}"
IsSelectedText="{Binding Path=IsSelectedText}"/>
ViewModel has a dumb property (no need to raise the change event since it is OneWayToSource)
public string TheSelectedText { get; set; }
And anywhere you can do
Console.WriteLine(TheSelectedText);
Upvotes: 4