【问题标题】:How to Workaround Referencing View's Controls如何解决引用视图的控件
【发布时间】:2012-09-16 07:06:46
【问题描述】:

我将 Galasoft 的 Light MVVM 用于我的 Siverlight 项目。

我已按照指示设置了所有内容:ViewModel 绑定到 ViewDataContext

我在View 中有一个名为inkCanvas 的画布。

ViewModel获取更新的项目数据时,我需要引用inkCanvas创建一个CanvasRender实例public CanvasRender(Canvas canvas, ProjectData pdata)

问题出在 MVVM 中,ViewModelView 一无所知,那么如何在View 中引用控件(inkCanvas)?

附: (已编辑):我所做的解决方法是:当我将项目数据传递给ViewModel 时,我也会从View 的代码隐藏中传递inkCanvas。嗯,现在我的代码隐藏不干净。

【问题讨论】:

  • 理想情况下,您不必从视图模型中引用特定视图,实际上应该避免。我可以确切地问​​一下您打算如何处理视图模型中的画布吗?
  • @KodeKreachor,我需要在来自类库的CanvasRender 中使用它。
  • 你打算如何处理 CanvasRender 方法中的画布?
  • @KodeKreachor,将所有项目数据渲染到画布上,CanvasRender来自另一个类库。
  • @PeterLee 在我看来,您可以接受 Cyber​​maxs 的仅使用绑定的建议,但扩展 Canvas 并将所有渲染代码放入扩展中。根据需要向 Canvas 扩展添加新的依赖属性。

标签: silverlight mvvm view viewmodel mvvm-light


【解决方案1】:

根据上述 cmets,一种方法是扩展 Canvas 并将对 CanvasRender 的引用保留在该类中。

public class MyCanvas : Canvas
{
    private CanvasRender _canvasRender;
    private ProjectData _data;

    public ProjectData Data
    {
        get { return _data; }       
        set
        {
            _data = value;
            _canvasRender = new CanvasRender(this, _data);
        }
    }

    public MyCanvas() : base()
    {
    }
}

您可能还想将ProjectData 设为一个依赖属性,以便它可以绑定。

这允许您维护 MVVM 模式,因为现在您可以用 XAML 编写:

<local:MyCanvas ProjectData="{Binding ViewModel.ProjectData}" />

【讨论】:

  • 好的。感谢您的回复。我会尽快尝试,并告诉你它是如何工作的。
  • 我扩展了我的画布,但现在我遇到了新问题,如何在MyCanvas 中获取CanvasRender。我需要公开另一个名为CanvasRender的属性?
  • @PeterLee 我真的不知道......我以为你会在扩展的 Canvas 中实例化 CanvasRender。您是说需要将其作为 XAML 属性绑定到扩展的 Canvas?
【解决方案2】:

在 MVVM 模式中,您不会直接在 ViewModel 中引用控件。在 MVVM 中,一切都是“绑定”的。您的 inkCanvas 将绑定到您的 ViewModel 中的一个属性。

public class MyViewModel : INotifyPropertyChanged
{
    private readonly StrokeCollection _mystrokes;

    public MyViewModel ()
    {
        _mystrokes= new StrokeCollection();
        (_mystrokesas INotifyCollectionChanged).CollectionChanged += delegate
        {
            //the strokes have changed
        };
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public StrokeCollection MyStrokes
    {
        get
        {
            return _mystrokes;
        }
    }

    private void OnPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;

        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

还有 XAML:

<InkCanvas Strokes="{Binding MyStrokes}"/>

编辑:

也许您的情况的解决方法是使用 EventToCommand :这允许直接在 XAML 中将 UI 事件绑定到 ICommand(并使用 Args 将引用传递给 inkCancas)

<i:Interaction.Triggers>
    <i:EventTrigger EventName="Loaded">
        <cmd:EventToCommand Command="{Binding Mode=OneWay, Path=LoadedCommand}"
                            PassEventArgsToCommand="True" />
    </i:EventTrigger>
</i:Interaction.Triggers>

【讨论】:

  • 感谢您的回复。但正如我非常清楚地指出,我的 CanvasRender 要求我传递 Canvas 参数,而不是 inkCanvas 的属性
  • 你能否让我更清楚?在哪里通过inkCanvas?而且为了调用它,我必须定义一个事件?
  • 您可以将 inkCanvas 的每个事件(Loaded、KeyUp、MouseDown...)绑定到带有 args 的自定义命令。使用 PassEventArgsToCommand=True,我认为您可以将 RoutedEventArgs(和发送者)传递给视图模型中的自定义命令。我没有时间测试这个,但我认为道路是明确的......
  • 不幸的是,当触发 Loaded 事件时,e.OriginalSource 始终为空。 public RelayCommand CanvasLoadedCommand { get { return new RelayCommand((e) => { theCanvas = e.OriginalSource as InkPresenter; }); } }
  • @peter 您可能应该使用 CommandParameter 属性而不是 PassEventArgsToCommand 属性。看我的回答。
【解决方案3】:

如果您要使用 EventToCommand 方法(您在另一个答案中尝试过),那么不要使用 PassEventArgsToCommand 属性,而是使用 CommandParameter 属性并将其绑定到您的 Canvas。

<i:Interaction.Triggers>
    <i:EventTrigger EventName="Loaded">
        <cmd:EventToCommand Command="{Binding Path=CanvasLoadedCommand}"
                            CommandParameter="{Binding ElementName=inkCanvas}" />
    </i:EventTrigger>
</i:Interaction.Triggers>

然后在您的 ViewModel 中:

public class ViewModel
{
    private Canvas m_canvas;

    public RelayCommand<Canvas> CanvasLoadedCommand { get; private set; }

    public ViewModel() 
    { 
        CanvasLoadedCommand = new RelayCommand<Canvas>(canvas =>  
        { 
            m_canvas = canvas;
        }); 
    }
}

所以一旦你的画布被加载,你应该在你的视图模型中保存对它的引用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多