【问题标题】:Executing command in another viewmodel?在另一个视图模型中执行命令?
【发布时间】:2014-06-10 21:03:38
【问题描述】:

我有一个从 ComboBox 派生的自定义“DateRangeSelector”控件。这是一个带有以下过滤器的下拉控件:
1. 今天
2. 未来三天
3. 未来三周
4. 自定义范围(允许用户设置自定义日期范围)

现在这个“DateRangeSelector”控件被添加到另一个 XAML(ActivityListMenuControlView.xaml) 中:

<DateRangeSelector:DateRangeSelectorControl x:Name="DateRangeSelector"
    Grid.Column="1"
    Margin="10 0 0 0"
    HorizontalAlignment="Left"
    VerticalAlignment="Center"
    AutomationProperties.AutomationId="AID_TaskListDateRangeSelector"
    DateRangeUpdatedCmd="{Binding Path=DateRangeSelectionUpdatedCommand}"
    TodayDateUpdatedCmd="{Binding Path=TodayDateUpdatedCommand}"
    FontSize="{StaticResource TaskListMenuFontSize}"
    RangeOptions="{Binding Path=DateRangeSelectionOptions,
    Mode=OneTime}"
    SelectedDateRange="{Binding Path=SelectedRange,
    Mode=TwoWay}"
    Visibility="{Binding Path=ShowFilterOptions,
    Converter={StaticResource boolToVisibility}}" />

从上面的代码可以明显看出,我在“DateRangeSelector”中创建了一个命令“TodayDateUpdatedCmd”,旨在在系统日期更改时更新此控件中的“Today”过滤器,并绑定到“ActivityListMenuControlViewModel”中的“TodayDateUpdatedCommand”命令。
更新日期的代码位于“DateRangeSelector”本身的方法“SetDateValues”中。 我只是对如何从“ActivityListMenuControlViewModel”执行此方法感到困惑? 请帮忙。

更新: DateRangeSelector 只是一个没有视图/视图模型的类。代码如下:

public class DateRangeSelectorControl : ComboBox, INotifyPropertyChanged
{
                  public static readonly DependencyProperty TodayDateUpdateCmdProperty = DependencyProperty.Register("TodayDateUpdatedCmd", typeof(ICommand), typeof(DateRangeSelectorControl),
    new PropertyMetadata(null));

    public ICommand TodayDateUpdatedCmd
    {
        get { return (ICommand)this.GetValue(TodayDateUpdateCmdProperty); }
        set
        {
            this.SetValue(TodayDateUpdateCmdProperty, value);
        }
    }

         /// <summary>
    /// 
    /// </summary>
    private void SetDateValues()
    {
        DateTime todaysDate = DateTime.Now;

        TodayText = Utility.GetStringFromResource("TodayLabel") + " (" + todaysDate.ToShortDateString() + ")";

        NextThreeDaysText = Utility.GetStringFromResource("NextThreeDaysLabel") + " (" + todaysDate.ToShortDateString() + " - " + todaysDate.AddDays(3).ToShortDateString() + ")";

        NextWeekText = Utility.GetStringFromResource("NextWeekLabel") + " (" + todaysDate.ToShortDateString() + " - " + todaysDate.AddDays(7).ToShortDateString() + ")";

        SetCustomDateRangeText();
    }
}

从上面的代码可以明显看出,我首先注册了一个依赖属性“TodayDateUpdateCmdProperty”和在“ActivityListMenuControlView.xaml”中使用的命令属性“TodayDateUpdatedCmd”,如 XAML sn-p 中所示。此外,我需要在 DateRangeSelector 类中执行方法“SetDateValues”来更新今天的日期。 现在请帮助我如何实现这一目标?

更新: 根据@GazTheDestroyer 的建议,我对代码进行了更改,现在没有使用任何命令。但是现在使用以下详细信息获取运行时 XamlParseException:

"'在类型 'VMS.Nexus.Client.Common.Controls.DateRangeSelector.DateRangeSelectorControl' 上调用与指定绑定约束匹配的构造函数引发了异常。'行号 '45' 和行位置 '14'。"}

内部异常: {"默认值类型与属性类型 'TodayDate' 不匹配。"}

在我创建 DateRangeSelector 的 ActivityListMenuControlView.xaml 中引发了此异常。 请帮忙

【问题讨论】:

    标签: c# wpf xaml mvvm icommand


    【解决方案1】:

    命令应该由 控件触发,而不是用作通知机制 您似乎正在尝试的控件。

    如果您的控件需要对一些变化的变量做出反应,那么它应该公开一个 DependencyProperty 并对它的变化做出反应。例如,在DateRangeSelectorControl的代码中

    public static readonly new DependencyProperty TodaysDateProperty=
                DependencyProperty.Register("TodaysDate", typeof(DateTime), typeof(DateRangeSelectorControl), new PropertyMetadata(null, TodaysDateChanged));
    
    public new DateTime TodaysDate
    {
        get { return (DateTime)GetValue(TodaysDateProperty); }
        set { SetValue(TodaysDateProperty, value); }
    }
    
    private static void TodaysDateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((DateRangeSelectorControl)d).TodaysDateChanged((DateTime)e.NewValue);
    }
    
    private void TodaysDateChanged(DateTime newDate)
    {
        //update your control here
    }
    

    然后,您的 ViewModel 可以简单地公开一个可以绑定到此 DependencyProperty 的 TodaysDate 属性。

    【讨论】:

    • 我没有为 DateRangeSelector 创建任何 ViewModel。它仅在 Themes\Generic.xaml 中定义,并且只是一个 .cs 文件(DateRangeSelector.cs)
    • 我知道。我发布的代码在 DateRangeSelector.cs 中,而不是视图模型中。我指的 ViewModel 将是您的 ActivityListMenuControlViewModel 或类似的。
    • “然后您的 ViewModel 可以简单地公开一个可以绑定到此 DependencyProperty 的 TodaysDate 属性。”当您发布此行时,如果我没有任何视图模型,我如何公开“TodaysDate”属性。如果你能在上下文中为“DateRangeSelector”和“ActivityListMenuControlViewModel”提供一些代码 sn-p 帮助我,那就太好了!
    • 正如我在之前的评论中所解释的,TodaysDate 将被 ActivityListMenuControlViewModel 公开。
    • 谢谢。我仍然有一个疑问,我已经将“ActivityListMenuControlView.xaml”中的绑定提供给“TodaysDate”(DateRangeSelector),其中的命令是“ICommand TodayDateUpdatedCommand”(在 ActivityListMenuControlViewModel 中定义),那么我现在应该如何继续?任何代码都会很有帮助。
    【解决方案2】:

    特别是在编写 WPF 和 MVVM 时,开发人员通常使用delegate ICommand 的形式,我们可以在视图模型中声明它。您可以在 MSDN 上WPF Apps With The Model-View-ViewModel Design Pattern 页面的中继命令逻辑部分找到流行的RelayCommand 的实现详细信息。您可以在 Stack Overflow 上的 How can I use the RelayCommand in wpf? 问题中找到其使用的基本示例。

    因此,您可以在您的视图模型中定义您的 ICommand 实例并将它们数据绑定到您的控件。对于您的DateRangeSelector,您可以简单地声明一个ICommand 类型的DependencyProperty,然后从您的视图模型中将数据绑定到它...可能是这样的:

    <DateRangeSelector:DateRangeSelectorControl TodayDateUpdatedCmd="{Binding Command}">
        ...
    </DateRangeSelector:DateRangeSelectorControl>
    

    在你的视图模型中:

    public ICommand Command
    {
        get { return new ActionCommand(action => DoSomething(),
            canExecute => CanDoSomething(); }
    }
    

    更新>>>

    很简单,没有必要在 WPF 的这个(或几乎任何)实例中扩展 ComboBox 类。请参阅 MSDN 上的 Control Authoring Overview 页面,了解有关为什么不需要这样做的更多信息。

    因此,基本思想是将功能从ComboBox 移至另一个类...视图模型类。然后,您可以从那里将数据绑定到ComboBox 并提供功能。在 WPF 中,我们操作的是 data,而不是 UIElement。因此,与其在控件中定义所有“过滤器”,不如在代码中定义它们。

    首先,您需要一个包含可用日期的集合属性:

    public ObservableCollection<DateTime> AvailableDates
    {
        get { return availableDates; }
        set { availableDates = value; NotifyPropertyChange("AvailableDates"); }
    }
    

    接下来,您应该有一个enum,其值代表每种过滤器类型。您可以将这种类型的属性添加到您的视图模型中,该属性可以控制每次更改时出现在ComboBox 中的项目:

    public FilterType FilterType
    {
        get { return filterType; }
        set
        {
            filterType = value; 
            NotifyPropertyChange("FilterType");
            FillAvailableDatesDependantOnFilterType();
        }
    }
    

    ...

    private void FillAvailableDatesDependantOnFilterType()
    {
        AvailableDates = new ObservableCollection<DateTime>();
        if (FilterType == "Today") AvailableDates.Add(DateTime.Now.Date);
        ...
    }
    

    希望你现在有一个更好的主意。

    【讨论】:

    • 请查看我为 DateRangeSelector 代码提供的更新。请注意,我没有为此定义任何视图模型。如果你能在 DateRangeSelector 和 ActivityListMenuControlViewModel 的上下文中帮助我一些代码 sn-p 会很棒!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-14
    • 2015-10-13
    相关资源
    最近更新 更多