【问题标题】:Pass value UserControl to ViewModel of View Page将值 UserControl 传递给 View Page 的 ViewModel
【发布时间】:2016-04-13 08:22:26
【问题描述】:

这是完整的Sample code
使用 MVVM 模式我的要求是有一个 ListView

  1. 如果用户在 ListView 内点击复选框 Storyboard Animation 应播放 True False 并且 ListView 绑定值应在数据库中更新。对于 true,刻度应该弹出动画,对于 false,刻度应该通过动画变得不可见。
    已解决根据@Elvis Xia 回答
  2. 如果用户点击 ListviewItem 导航到具有值的新页面
  3. Blueprint

现在我开始为数据模板创建用户控件。在这里,我想分别识别这两个事件,用户分别单击 checkbox 或单击 Item
使用 ICommand 我正在创建两个委托,它们绑定到两个透明按钮,该按钮中继被点击的事件。
创建透明按钮并在绑定它们的同时创建委托让我确信肯定有更好的方法可以利用 MVVM 来处理这些事件,而无需任何代码

用户控件 XAML

<Button Background="LightBlue" BorderBrush="White" BorderThickness="4" Command="{x:Bind sampleItem.itemTapped}" CommandParameter="{Binding}" HorizontalContentAlignment="Stretch">

    <Grid Margin="20">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <TextBlock Margin="20" HorizontalAlignment="Center" Text="{x:Bind sampleItem.sampleText}" FontSize="30"/>
        <Image Grid.Column="1" Height="60" Width="60" Source="ms-appx:///Assets/check_off.png" HorizontalAlignment="Right"/>
        <Image x:Name="image" Grid.Column="1" Height="60" Width="60" Source="ms-appx:///Assets/check_on.png" HorizontalAlignment="Right"  Visibility="Collapsed" RenderTransformOrigin="0.5,0.5">
            <Image.RenderTransform>
                <CompositeTransform/>
            </Image.RenderTransform>
        </Image>
        <Button x:Name="btnFav" Grid.Column="1"  Height="60" Width="60" HorizontalAlignment="Right" Background="Transparent" Command="{x:Bind sampleItem.favTapped}" CommandParameter="{Binding}">
            <Interactivity:Interaction.Behaviors>
                <!--<Core:EventTriggerBehavior EventName="Tapped">
                <Core:InvokeCommandAction Command="{Binding favTapped}" />
            </Core:EventTriggerBehavior>-->
                <Core:DataTriggerBehavior Binding="{Binding isFav}" Value="true">
                    <Media:ControlStoryboardAction Storyboard="{StaticResource StoryboardCheckOn}"/>
                </Core:DataTriggerBehavior>
                <Core:DataTriggerBehavior Binding="{Binding isFav}" Value="false">
                    <Media:ControlStoryboardAction Storyboard="{StaticResource StoryboardCheckOff}"/>
                </Core:DataTriggerBehavior>
            </Interactivity:Interaction.Behaviors>
        </Button>
    </Grid>
</Button>

UserControl XAML 代码隐藏

MainPageModel sampleItem { get { return this.DataContext as MainPageModel; } }
        public MainPageUserControl()
        {
            this.InitializeComponent();
            this.DataContextChanged += (s, e) => this.Bindings.Update();
        }

视图模型代码

public async Task GetData()
{
    for (int i = 0; i < 10; i++)
    {
        if (i == 3)
            sampleList.Add(new MainPageModel { sampleText = "Selected", isFav = true, favTapped= new DelegateCommand<MainPageModel>(this.OnFavTapped), itemTapped= new DelegateCommand<MainPageModel>(this.OnItemTapped)});
        else
            sampleList.Add(new MainPageModel { sampleText = "UnSelected"+i.ToString(), isFav = null, favTapped = new DelegateCommand<MainPageModel>(this.OnFavTapped), itemTapped = new DelegateCommand<MainPageModel>(this.OnItemTapped) });
}
}
private void OnFavTapped(MainPageModel arg)
{
    if (arg.isFav == null) arg.isFav = true;
    else
        arg.isFav = !arg.isFav;
}
private void OnItemTapped(MainPageModel arg)
{
    System.Diagnostics.Debug.WriteLine("Button Value: "+arg.sampleText);
    System.Diagnostics.Debug.WriteLine("Selected Item Value: "+selectedItem.sampleText);
}

MainPage Xaml

 <Grid Grid.Row="1">
            <ListView  ItemsSource="{x:Bind ViewModel.sampleList}" IsItemClickEnabled="True" SelectedItem="{Binding ViewModel.selectedItem,Mode=TwoWay}">
                <ListView.ItemTemplate>
                    <DataTemplate>                       
                            <userControls:MainPageUserControl/>                       
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </Grid>

必须有更好的方法来使用后面的代码来达到预期的结果。

【问题讨论】:

  • 如果您在 ViewModel 中执行此操作,这是一个很好的方法。是的,你必须执行命令。如果您使用 PRISM,它已经实现了。
  • 您使用 DataTriggerBehaviour 播放情节提要的权利。我不明白为什么你必须在 viewmodel 中访问它。
  • 我不明白你问题的第二部分。你能详细说明一下吗?
  • 如果您想在单击一个按钮时将项目设为选中-> 在视图模型中具有属性。使用 TwoWay 绑定将其绑定到 ListView 的 SelectedItem。单击按钮时,获取按钮的 DataContext 并将其分配给 viewmodel 的 SelectedItem 属性
  • 您不必删除 UserControl。使用它,您可以将其绑定到 Viewmodel。如果您对此有任何疑问,请告诉我。现在我不在电脑前。所以我看不到你的样品。但我之前看过它

标签: c# mvvm user-controls win-universal-app windows-10-universal


【解决方案1】:
public class ViewMOdel()
{ 
       public ViewModel()
    {
favTapped= new DelegateCommand<MainPageModel>(this.OnFavTapped)
 itemTapped= new DelegateCommand<MainPageModel>(this.OnItemTapped)
    }

public async Task GetData()
    {
        for (int i = 0; i < 10; i++)
        {
            if (i == 3)
                sampleList.Add(new MainPageModel { sampleText = "Selected", isFav = true});
            else
                sampleList.Add(new MainPageModel { sampleText = "UnSelected"+i.ToString(), isFav = null});
    }
    }
    private void OnFavTapped(MainPageModel arg)
    {
        if (arg.isFav == null) arg.isFav = true;
        else
            arg.isFav = !arg.isFav;
    }
    private void OnItemTapped(MainPageModel arg)
    {
        System.Diagnostics.Debug.WriteLine("Button Value: "+arg.sampleText);
        System.Diagnostics.Debug.WriteLine("Selected Item Value: "+selectedItem.sampleText);
    }
    }

绑定应该是这样的

<Button x:Name="btnFav" Grid.Column="1"  Height="60" Width="60" HorizontalAlignment="Right" Background="Transparent" Command="{Binding ElementName=UserControl, Path=Tag.favTapped}" CommandParameter="{Binding}"/>

更新

<ListView  ItemsSource="{x:Bind ViewModel.sampleList}"  x:Name="Listview"IsItemClickEnabled="True" SelectedItem="{Binding ViewModel.selectedItem,Mode=TwoWay}">
                    <ListView.ItemTemplate>
                        <DataTemplate>                       
                                <userControls:MainPageUserControl Tag="{Binding DataContext,ElementName=Listview}"/>                       
                        </DataTemplate>
                    </ListView.ItemTemplate>
                </ListView>
 <Button x:Name="btnFav" Grid.Column="1"  Height="60" Width="60" HorizontalAlignment="Right" Background="Transparent" Command="{Binding ElementName=UserControl, Path=Tag.favTapped}" CommandParameter="{Binding}"/>

Update2 使用 EventTriggerBehavior

 favTapped = new DelegateCommand<RoutedEventArgs>(this.OnFavTapped);

private void OnFavTapped(RoutedEventArgs arg)
        {
         var item =  (( arg.OriginalSource )as Button).DataContext as MainPageModel
        }

<Button n x:Name="btnFav" Grid.Column="1"  Height="60" Width="60" HorizontalAlignment="Right" Background="Transparent"   >
            <interact:Interaction.Behaviors>

                <interactcore:EventTriggerBehavior EventName="Click" >
                    <interactcore:InvokeCommandAction Command="{Binding ElementName=usercontrol, Path=Tag.favTapped}" />
                </interactcore:EventTriggerBehavior>
            </interact:Interaction.Behaviors>
        </Button>

【讨论】:

  • 我的 userControl Datacontext 是 MainPageModel,从“后面的代码”可以看出。如果我将 datacontext 设置为 MainPageViewModel 那么我将如何绑定。我的层次结构是 Listview(带有可观察的集合绑定)--> Itemtemplate 是 Usercontrol(它将绑定作为 MainPageModel)。您的解决方案是我尝试的第一件事,但问题是在哪里定义命令。
  • 好的。我不确定试试这个。将 ListViewname 作为 elementname 而不是 usercontrolname
  • 是的。我说在Usercontrol里面。使用 {Binding ElementName=ListviewName,Path=DataContext.favTapped}。
  • 检查更新。我使用了 Tag 属性。我也测试过。它有效
  • 是的,它奏效了。这需要为 listview 和 usercontrol 指定名称。那么我应该使用我之前使用 {x:Bind} 的方法还是这个方法。最近引入了编译时绑定,但应该有一种方法来实现它也是正确的
【解决方案2】:

项目中每个项目的 DataContext 都是MainPageModel 类的实例。所以 favTapped 命令应该添加到MainPageModel 类中。而且是一个命令,所以favTapped应该是一个新类的实例,它实现了ICommand接口。

如果您不希望动画在页面首次加载时显示,您可以将isFav 设置为bool?。并且在页面第一次加载时,将isFav的值设置为null,这样就不会触发动画动作了。

以下是代码 sn-ps 和演示链接:

ViewModelCommands.cs:

public class ViewModelCommands : ICommand
{
    public event EventHandler CanExecuteChanged;

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        //if it's a tapped event
        if (parameter is TappedRoutedEventArgs)
        {
            var tappedEvent = (TappedRoutedEventArgs)parameter;
            var gridSource = (Grid)tappedEvent.OriginalSource;
            var dataContext = (MainPageModel)gridSource.DataContext;
            //if tick is true then set to false, or the opposite.
            if (dataContext.isFav == null)
            {
                dataContext.isFav = true;
            } else
            {
                dataContext.isFav = !dataContext.isFav;
            }
        }
    }
}

MainPageModel.cs:

 public class MainPageModel:BindableBase
{

    public MainPageModel() {
        favTapped = new ViewModelCommands();
    }
    public ViewModelCommands favTapped { get; set; }
    private string _sampleText;
    public string sampleText
    {
        get
        {
            return this._sampleText;
        }
        set
        {
            Set(ref _sampleText, value);
        }
    }
    private bool? _isFav;
    public bool? isFav
    {
        get
        {
            return this._isFav;
        }
        set
        {
            Set(ref _isFav, value);
        }
    }
}

这是完整的演示:Demo Project

更新:

使用DelegateCommand 时,您可以将命令属性添加到 MainPageModel.cs 中,因为项目的 DataContext 是 MainPageModel 实例。您可以使用this.isFav 更改点击项的isFav 值。

MainPageModel.cs 的代码如下:

public class MainPageModel : BindableBase
{
    private DelegateCommand _favTapped;

    public DelegateCommand favTapped
    {
        get
        {
            if (_favTapped == null)
            {
                _favTapped = new DelegateCommand(() =>
                {
                    //Here implements the check on or off logic
                    this.CheckOnOff();
                }, () => true);
            }
            return _favTapped;
        }
        set { _favTapped = value; }
    }

    private void CheckOnOff()
    {
        if (this.isFav == null)
        {
            this.isFav = true;
        }
        else
        {
            this.isFav = !this.isFav;
        }
    }
    private string _sampleText;
    public string sampleText
    {
        get
        {
            return this._sampleText;
        }
        set
        {
            Set(ref _sampleText, value);
        }
    }
    private bool? _isFav;
    public bool? isFav
    {
        get
        {
            return this._isFav;
        }
        set
        {
            Set(ref _isFav, value);
        }
    }
}

对于已选择的列表视图项

您可以使用 ListView.ItemClick 事件。但是你也应该设置 IsItemClickEnabled="True",否则事件处理程序不会被触发。

对于点击的Listview子项

你可以使用 userControl 的 Tapped Event。

这里是 Xaml 代码,展示了如何注册上述两个事件:

<Grid Grid.Row="1">
    <ListView IsItemClickEnabled="True" ItemClick="ListView_ItemClick_1" ItemsSource="{x:Bind ViewModel.sampleList}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <userControls:MainPageUserControl  Tapped="MainPageUserControl_Tapped"/>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</Grid>

【讨论】:

  • 在这里,您如何建议我能够区分 1) Listview item selected 2) Listview 的子项点击 3) SelectedItem 的三个事件点击。此外,模板 10 已经有一个预定义的 ICommand,我将其用作 DelegateCommand&lt;MainPageModel&gt;(this.OnFavTapped) 如果您可以查看上面 cmets 中的聊天讨论,您会发现我达到的解决方法。您能否提出一个更好的方法来识别这三个事件
  • ListView itemselected 和 selecteditem 是什么意思?您可以使用 selectionchanged 事件来获取 Selecteditem 或者您可以使用 ListView 的项目单击事件
  • @Jerin 我已经更新了答案。希望对您有所帮助。目前我无法上传我的项目。如果你愿意,我可以稍后上传。
  • 我不想使用后面的代码,但只使用我的视图模型。除了我当前的样本之外,还有什么更好的方法可以做到这一点。 @Archana我已经用最新版本更新了这个问题。有没有更好的办法。
猜你喜欢
  • 2016-07-07
  • 2021-06-08
  • 1970-01-01
  • 2012-05-13
  • 1970-01-01
  • 2019-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多