bene_rawr
bene_rawr

Reputation: 119

WPF MVVM Interaction Binding CommandParameter to UI element

I want the text in a TextBox to get selected when the TextBox gets focused. Therefore I need to binding a Command to the "GotFocus" event. The special thing is, that the TextBox is created dynamically via an ItemsControl. So there is a binding to the UserControl (View), the ItemsControl and the Item itself. When I tried to bind the UI element to the CommandParameter I just got the Model bindet to the current item in the ItemsControl.

All the bindings are working perfectly except the CommandParameter..

Somebody got an idea how to get this working?

Here is my code:

XAML

/////// <UserControl/> Information:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
x:Name="MainBindingControl"
///////

    <ItemsControl ItemsSource="{Binding MySecondModelList }" Margin="10,10,10,0">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Grid helper:GridHelper.RowCount="{Binding MyFirstModel.Rows}" helper:GridHelper.ColumnCount="{Binding MyFirstModel.Columns}">
                </Grid>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemContainerStyle>
            <Style>
                <Setter Property="Grid.Row" Value="{Binding Row}" />
                <Setter Property="Grid.Column" Value="{Binding Column}" />
            </Style>
        </ItemsControl.ItemContainerStyle>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <TextBox Margin="25,25,25,25" Height="30" Width="30" Text="{Binding Text, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" TextAlignment="Center" VerticalContentAlignment="Center" >
                    <i:Interaction.Triggers>
                        <i:EventTrigger EventName="GotFocus">
                            <i:InvokeCommandAction Command="{Binding ElementName=MainBindingControl, Path=DataContext.TextBoxFocusCommand}" CommandParameter="{Binding RelativeSource={ RelativeSource Self }}"/>
                        </i:EventTrigger>
                    </i:Interaction.Triggers>
                <TextBox.Style>
                    <Style TargetType="TextBox">
                        <Setter Property="Background" Value="OrangeRed" />
                            <Style.Triggers>
                                <Trigger Property="Text" Value="0">
                                    <Setter Property="Background" Value="Orange" />
                                </Trigger>
                                <Trigger Property="Text" Value="1">
                                    <Setter Property="Background" Value="White" />
                                </Trigger>
                                <Trigger Property="Text" Value="2">
                                    <Setter Property="Background" Value="White" />
                                </Trigger>
                                <Trigger Property="Text" Value="3">
                                    <Setter Property="Background" Value="White" />
                                </Trigger>
                                <Trigger Property="Text" Value="4">
                                    <Setter Property="Background" Value="White" />
                                </Trigger>
                            </Style.Triggers>
                        </Style>
                    </TextBox.Style>
                </TextBox>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

CS

    #region TextBoxFocus
    private ICommand _textBoxFocusCommand;
    public ICommand TextBoxFocusCommand
    {
        get { return _textBoxFocusCommand; }
        set { _textBoxFocusCommand = value; }
    }
    public void TextBoxFocus(object parameter)
    {
        var _tmp = parameter as TextBox;
        if (_tmp != null )
        {
            _tmp.SelectAll();
        }
    }
    #endregion

Models

public class FirstModel
    {
        public int Rows { get; set; }
        public int Columns { get; set; }
    }

public class SecondModel
    {
        public int Row { get; set; }
        public int Column { get; set; }
        public string Text { get; set; }
    }

public class ViewModel
    {
        public FirstModel MyFirstModel { get; set; }
        public ObservableCollection<SecondModel> MySecondModelList { get; set; }
    }

Upvotes: 0

Views: 2109

Answers (1)

Daniel Marques
Daniel Marques

Reputation: 703

Since what you want to do is only related to the view, I'd just add the code in the code-behind, instead of trying to use commands and get the TextBox inside the ViewModel. In MVVM you should NEVER reference UI assemblies from the ViewModel. But it is ok for you to use code-behind if what you are trying to do is only related to the View.

So, inside the style of the TextBox, you would have:

<EventSetter Event="GotFocus" Handler="TextBox_GotFocus"/>

And then in the code-behind of the UserControl:

private void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
    TextBox textBox = sender as TextBox;
    textBox.SelectAll();
}

The complete code of your DataTemplate would then be:

<DataTemplate>
    <TextBox Margin="25,25,25,25" Height="30" Width="30" Text="{Binding Text, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" TextAlignment="Center" VerticalContentAlignment="Center" >
        <!-- Just erase this block of code
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="GotFocus">
                <i:InvokeCommandAction Command="{Binding ElementName=MainBindingControl, Path=DataContext.TextBoxFocusCommand}" CommandParameter="{Binding RelativeSource={ RelativeSource Self }}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>-->
        <TextBox.Style>                            
            <Style TargetType="TextBox">
                <EventSetter Event="GotFocus" Handler="TextBox_GotFocus"/>
                <Setter Property="Background" Value="OrangeRed" />
                <Style.Triggers>
                    <Trigger Property="Text" Value="0">
                        <Setter Property="Background" Value="Orange" />
                    </Trigger>
                    <Trigger Property="Text" Value="1">
                        <Setter Property="Background" Value="White" />
                    </Trigger>
                    <Trigger Property="Text" Value="2">
                        <Setter Property="Background" Value="White" />
                    </Trigger>
                    <Trigger Property="Text" Value="3">
                        <Setter Property="Background" Value="White" />
                    </Trigger>
                    <Trigger Property="Text" Value="4">
                        <Setter Property="Background" Value="White" />
                    </Trigger>
                </Style.Triggers>
            </Style>
        </TextBox.Style>
    </TextBox>
</DataTemplate>

Notice that the method SelectAll() of the TextBox called on GotFocus event has a little trick to work as intended. Check this SO question: How to automatically select all text on focus in WPF TextBox?

Upvotes: 0

Related Questions