将值 UserControl 传递给视图页面的 ViewModel

Pass value UserControl to ViewModel of View Page

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

  1. 如果用户在 ListView 内部点击复选框 故事板动画 应该播放 True False 并且 ListView 绑定值应该更新在数据库中。对于 true,tick 应该弹出动画 false,tick 应该随着动画变得不可见。
    Solved as per @Elvis Xia answer
  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>

用户控件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);
}

主页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>

必须有更好的方法来使用隐藏代码实现预期结果。

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

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

下面是代码片段和演示 Link:

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);
        }
    }
}

For Listview item selected

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

For The subitem of Listview tapped

您可以使用用户控件的点击事件。

这里是 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>
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>