How to set focus to textbox using MVVM?

2019-01-11 11:07发布

How to focus a textbox from ViewModel wpf?

<TextBox Name="PropertySearch"
         Text="{Binding UpdateSourceTrigger=PropertyChanged, 
                        Mode=TwoWay, Path=PropertySearch, 
                        ValidatesOnDataErrors=True}"  
         Width="110" 
         Height="25" 
         Margin="10" />

标签: c# wpf xaml mvvm
4条回答
疯言疯语
2楼-- · 2019-01-11 11:35

While purists may argue for leaving this out of the VM, there are cases where it may make sense to do so from the VM.

My approach has been to make the view implement an interface, pass that interface to the ViewModel, and then let the VM call methods on the interface.

Example:

public interface IFocusContainer
{
   void SetFocus(string target);
}

A couple things to keep in mind:

  1. A VM might serve more than one instance of a view, so your VM might want to have a collection of references to IFocusContainer instances, not just one.
  2. Code the VM defensively. You don't know whether there are 0, 1 or 20 views listening.
  3. The "target" parameter of SetFocus() should probably be "loosely" coupled to the VM. You don't want the VM caring about the exact control names in the UI. Rather, the VM should indicate a name that is defined solely for focus management. In my case, I created some attached properties that would allow me to "tag" controls with "focus names".

To implement the interface, you can:

  1. Implement it in the code-behind
  2. Create some behaviors that know how to attach to the ViewModel that is present in the DataContext.

There's nothing wrong with implementing it on the Code Behind, but the behavior approach does allow a XAML only hookup if that's important to you.

In the implementation of the interface, you can use the visual tree to locate the control, or you could just code up a switch statement for a known set of focusable items.

查看更多
Bombasti
3楼-- · 2019-01-11 11:38

The question you should be asking yourself is "why does my ViewModel need to know which control has the focus?"

I'd argue for focus being a view-only property; it's an interaction property, and has nothing to do with the conceptual state. This is akin to the background color of a control: why would you represent it in the VM? If you need to manage the focus in a custom way, it's probably better to use a view-level object to do the job.

查看更多
乱世女痞
4楼-- · 2019-01-11 11:38

In your parent control, add the following property:

FocusManager.FocusedElement="{Binding ElementName=PropertySearch}" 
查看更多
成全新的幸福
5楼-- · 2019-01-11 11:54

You can do this by adding a property to your ViewModel (or use an existing property) that indicates when the SetFocus should happen but the View should be responsible for actually setting the focus since that is purely View related.

You can do this with a DataTrigger.

View:

<Grid Name="LayoutRoot" DataContext="{StaticResource MyViewModelInstance}">
    <Grid.Style>
        <Style>
            <Style.Triggers>
                <DataTrigger Binding="{Binding UserShouldEditValueNow}" Value="True">
                    <Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=PropertySearch}"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Grid.Style>
    <TextBox   Name="PropertySearch"   Text="{Binding UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, Path=PropertySearch, ValidatesOnDataErrors=True}" Width="110" Height="25" Margin="10" />
</Grid>

ViewModel:

// When you think the view should set focus on a control
this.UserShouldEditValueNow = true;

The example above is simplified by just using a boolean ViewModel property "UserShouldEditValueNow". You can add a property like this to your ViewModel or use some other exising property that indicates this state.

Note: So why is it done this way in MVVM? One reason is, suppose the View author decided to replace the TextBox with a ComboBox, or even better, suppose your property was an integer value that had both a TextBox to view/edit the number and a Slider as another way to edit the same value, both controls bound to the same property... how would the ViewModel know which control to set focus on? (when it shouldn't even know what control, or controls, are bound to it in the first place) This way the View can select which control to focus by changing the ElementName binding target in the DataTrigger Setter.

Happy coding!

查看更多
登录 后发表回答