如何在MVVM中链接用户控件和main之间的命令

时间:2017-07-07 13:59:33

标签: c# wpf xaml mvvm

我有一个用户控件,其中一个TextBox和一个Button名为View。 TextBox获取索引值。

在我的Main视图中,我有一个列表视图,它将显示文件中的所有行。 ObservableCollection与此绑定。

我需要的是,当在TextBox中输入索引值并且点击了视图Button时(在用户控件中),SelecedIndex ListView(在Main中)应更改为索引值。

如何使用MVVM实现这一目标?

此外,如果我做错了,请提供正确的方法。

这是我的UserControl代码:

XAML

<UserControl.DataContext>
    <VM:IndexView_VM ></VM:IndexView_VM>
</UserControl.DataContext>
<Grid Background="White">
    <TextBlock Margin="10,12,168,9" Text="Index : "/>
    <TextBox Text="{Binding Index}" x:Name="TB_Index" Margin="53,11,90,8" />
    <Button Command="{Binding View_CMD}" x:Name="BT_View" Content="View" Margin="136,11,10,8" />
</Grid>

视图模型

public class IndexView_VM : ViewModelBase
{
    public IndexView_VM()
    {
        View_CMD = new RelayCommand(_View_CMD);
    }

    int _Index;
    public int Index
    {
        get { return _Index; }
        set
        {
            _Index = value;
            RaisePropertyChanged();
        }
    }

    public RelayCommand View_CMD { get; set; }
    internal void _View_CMD(object Parameter)
    {
        // What to write here?
    }
}

以下是主视图:

XAML

<UserControl.DataContext>
        <VM:MainView_VM></VM:MainView_VM>
    </UserControl.DataContext>
    <Grid Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="111*"/>
            <ColumnDefinition Width="100*"/>
        </Grid.ColumnDefinitions>
        <StackPanel>
            <local:IndexView/>
            <local:IndexView/>
            <local:IndexView/>
            <local:IndexView/>
        </StackPanel>
        <ListView ItemsSource="{Binding FileData}" x:Name="listView" Grid.Column="1" >
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Data" Width="100"/>
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>

查看模型

public class MainView_VM : ViewModelBase
{
    public MainView_VM()
    {
        ReadFile();
    }

    public ObservableCollection<string> FileData { get; set; }

    void ReadFile()
    {
        //I will read file here.
    }
}

3 个答案:

答案 0 :(得分:0)

如果您使用的是MvvmLight,您可以使用Messenger类将子视图模型中的消息发送给父级:

<强> IndexView_VM:

internal void _View_CMD(object Parameter)
{
    GalaSoft.MvvmLight.Messaging.Messenger.Default.Send(_Index);
}

<强> MainView_VM:

public class MainView_VM : ViewModelBase
{
    public MainView_VM()
    {
        ReadFile();
        GalaSoft.MvvmLight.Messaging.Messenger.Default.Register<int>(this, index => Index = index);
    }

    public ObservableCollection<string> FileData { get; set; }

    int _Index;
    public int Index
    {
        get { return _Index; }
        set
        {
            _Index = value;
            RaisePropertyChanged();
        }
    }

    void ReadFile()
    {
        //I will read file here.
    }
}

<强>的MainView:

<ListView ItemsSource="{Binding FileData}" x:Name="listView" Grid.Column="1"
          SelectedIndex="{Binding Index}">
    <ListView.View>
        <GridView>
            <GridViewColumn Header="Data" Width="100"/>
        </GridView>
    </ListView.View>
</ListView>

有关Messenger的更多信息,请参阅Laurent Bugnion的MSDN杂志文章。

MVVM - MVVM中的Messenger和View Services: https://msdn.microsoft.com/en-us/magazine/jj694937.aspx

答案 1 :(得分:0)

不确定我是否理解这一点。您想使用您在ListBox中输入的索引值设置TextBox的所选项目,这是对吗?

如果这是正确的,那么我有以下注意事项:

  1. 在这种情况下,UserControl可能有点过分。 特别是,如果你永远不会在你的UI中的其他地方重复使用它。
  2. 首先要考虑的是,您只需将TextBox移至主视图,为其命名并将.Text属性绑定到ListView的{​​{1}} SelectedIndex 1}}财产。
  3. 示例:

    <ListView SelectedIndex="{Binding ElementName=txtIndex, Path=Text, Converter={StaticResource StringToIntConverter}, UpdateSourceTrigger=PropertyChanged}" />
    

    需要转换器(您需要实现),因为将string转换为int不会自动发生。 UpdateSourceTrigger=PropertyChanged会在您输入后立即更新目标属性,无需按钮。

    此时你应该完成。

    如果您绝对需要UserControl,则可以添加DependencyProperty公开TextBox的值。然后你可以绑定到该属性,如上例所示。

答案 2 :(得分:0)

以下是您要执行的操作的基本示例:

Window XAML

<Window.Resources>
    <local:StringToIntConverter x:Key="StringToIntConverter" />
</Window.Resources>
<StackPanel>
    <TextBlock>Select Index</TextBlock>
    <TextBox x:Name="theTextBox" Text="{Binding SelectedIndex,ElementName=theList,Converter={StaticResource StringToIntConverter},Mode=OneWayToSource,UpdateSourceTrigger=Explicit}" />
    <Button Click="Button_Click">Select Now</Button>
    <ListBox x:Name="theList">
        <ListBoxItem>First</ListBoxItem>
        <ListBoxItem>Second</ListBoxItem>
        <ListBoxItem>Third</ListBoxItem>
    </ListBox>
</StackPanel>

Window CodeBehind

private void Button_Click(object sender, RoutedEventArgs e)
{
    theTextBox.GetBindingExpression(TextBox.TextProperty)?.UpdateSource();
}

<强>解释

您要求通过单击按钮从文本框中设置列表中的索引。我们使用几个设置将TextBox绑定到选定的索引:

  • 来源:SelectedIndex
  • ElementName:列表/目标UI元素
  • 转换器:从String到Int(我自己写的)
  • 是必需的
  • 模式:OneWayToSource,我们强制文本框只发送值到列表而不是反过来
  • UpdateSourceTrigger:我们不希望绑定自动更新,我们想自己做这个

要更新绑定,我们使用按钮的Click事件。

但视图模型呢? 该操作是一个View-Only操作,ViewModel不需要知道任何关于它的信息,因此我们应该将其排除在操作之外。这就是我没有使用CommandBinding的原因。

哎呀,忘了UserControl 如果你想把它放在一个用户控件中,那么我建议你根本不创建一个ViewModel。同样在用户控件中,您不需要DataBinding,仅在外部。保持简单:

UserControl XAML

<TextBlock>Select Index</TextBlock>
<TextBox x:Name="theTextBox" />
<Button Click="Button_Click">Select Now</Button>

UserControl CodeBehind

public int Index
{
    get { return (int)GetValue(IndexProperty); }
    set { SetValue(IndexProperty, value); }
}

public static readonly DependencyProperty IndexProperty =
        DependencyProperty.Register("Index", typeof(int), typeof(ListViewIndexSelectorControl), new PropertyMetadata(0));
private void Button_Click(object sender, RoutedEventArgs e)
{
    if(int.TryParse(theTextBox.Text, out int result))
    {
        Index = result;
    }
}

MainWindow用法XAML

<local:ListViewIndexSelectorControl Index="{Binding SelectedIndex,ElementName=theList,Mode=OneWayToSource}" />
<ListBox x:Name="theList">
    <ListBoxItem>First</ListBoxItem>
    <ListBoxItem>Second</ListBoxItem>
    <ListBoxItem>Third</ListBoxItem>
</ListBox>

如果您以后需要ViewModel,您也可以使用View作为ViewModel进行简单控制,只需在View的构造函数中设置DataContext = this;或使用XAML元素上的Name并绑定ElementName的DataContext。