【问题标题】:Coding multiple viewmodels in WPF XAML MVVM在 WPF XAML MVVM 中编码多个视图模型
【发布时间】:2013-01-12 22:48:40
【问题描述】:

对于我的项目,我正在尝试创建一个连接到工具集并将信号推送到其中的信号通道生成器。

我遇到的问题是,我以文本框的代码在代码隐藏文件中的形式给出了项目,我希望它们在 xaml 中。

我有一个变量来控制可以更改的通道数(视图模型)。它能够在窗口上创建同一视图模型的多个实例。这允许在工具内选择与之通信的不同目标,并能够将信号泵送到每个目标。

这是当前 XAML 中的代码:

    <Window x:Class="SigGeneratorMVVM.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:SigGeneratorMVVM"
        Title="Signal Generator" Height="370" Width="734" >
    <StackPanel Name="MyWindow">
        <!--<TextBox Height="23" HorizontalAlignment="Left" Margin="91,20,0,0" Name="CurrentValDisplay" VerticalAlignment="Top" Width="120" />-->
    </StackPanel>
</Window>

这是 mainwindow.cs 的代码

public partial class MainWindow : Window
    {
        List<ViewModel> gViewModels;

        int gNumChannels = 1;
        private System.Threading.Timer mViewUpdateTimer;
        private TimerCallback mViewTimerCallback;

        private UtilityParticipant mParticipant;

    public MainWindow()
    {
        InitializeComponent();

        // Connect as UtilityParticipant            
        ConnectMesh();

        gViewModels = new List<ViewModel>();           

        for (int i = 0; i < gNumChannels; i++)
        {
            gViewModels.Add(new ViewModel(mParticipant));

            TextBlock CurrentValueText = new TextBlock();
            CurrentValueText.Text = "Current Value:";
            CurrentValueText.Margin = new Thickness(5);

            TextBox CurrentValueBox = new TextBox();
            CurrentValueBox.Width = 120;
            CurrentValueBox.Name = "CurrentValDisplay" + i.ToString();
            CurrentValueBox.HorizontalAlignment =                                                System.Windows.HorizontalAlignment.Left;
            CurrentValueBox.Margin = new Thickness(10);
            CurrentValueBox.SetBinding(TextBox.TextProperty, "CurrentValue");

            //CurrentValDisplay.Name = "CurrentValDisplay" + i.ToString();
            //CurrentValDisplay.SetBinding(TextBox.TextProperty, "CurrentValue");

            TextBlock CurrentFrequencyText = new TextBlock();
            CurrentFrequencyText.Text = "Frequency:";
            CurrentFrequencyText.Margin = new Thickness(5);

            TextBox CurrentFrequencyBox = new TextBox();
            CurrentFrequencyBox.Width = 120;
            CurrentFrequencyBox.Name = "CurrentFrequencyDisplay" + i.ToString();
            CurrentFrequencyBox.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
            CurrentFrequencyBox.Margin = new Thickness(10);
            CurrentFrequencyBox.SetBinding(TextBox.TextProperty, "Frequency");

            Slider FrequencySlider = new Slider();
            FrequencySlider.Width = 200;
            FrequencySlider.Name = "FrequencySet" + i.ToString();
            FrequencySlider.Value= 10;
            FrequencySlider.Maximum = 10;
            FrequencySlider.Minimum = 0.1;
            FrequencySlider.SetBinding(Slider.ValueProperty, "Frequency");                    

            //Create a new stackpanel
            StackPanel sp = new StackPanel();
            sp.Orientation = Orientation.Vertical;

            //Set DataContext of the StackPanel
            sp.DataContext = gViewModels[i];

            //Add controls created above to the StackPanel
            sp.Children.Add(CurrentValueText);
            sp.Children.Add(CurrentValueBox);
            sp.Children.Add(CurrentFrequencyText);
            sp.Children.Add(CurrentFrequencyBox);
            sp.Children.Add(FrequencySlider);

            //Add the StackPanel to the window

            MyWindow.Children.Add(sp);

        }

        mViewTimerCallback = this.UpdateView;
        mViewUpdateTimer = new System.Threading.Timer(mViewTimerCallback, null,     100, 20);
    }    

更新:我已经有一个 ViewModel,它为每个属性(现在是 CurrentValue 和 Frequency)设置了方法,将 DataTemplate 和 ItemsControl 绑定到它就足够了,而不是创建一个新的模型类?

    private SigGenChannel mSigGenChannel;

    //Constructor
    public ViewModel(UtilityParticipant aParticipant)
    {
        mSigGenChannel = new SigGenChannel(aParticipant);
    }


    public string CurrentValue
    {
        get
        {
            return mSigGenChannel.CurrentValue.ToString();
        }

        set
        {
            mSigGenChannel.CurrentValue = double.Parse(value);
            RaisePropertyChanged("CurrentValue");
        }
    }

    public double Frequency
    {
        get
        {
            return mSigGenChannel.Frequency;
        }

        set
        {
            mSigGenChannel.Frequency = value;
            RaisePropertyChanged("Frequency");
        }
    }

    public double Amplitude
    {
        get
        {
            return mSigGenChannel.Amplitude;
        }

        set
        {
            mSigGenChannel.Amplitude = value;
            RaisePropertyChanged("Amplitude");
        }
    }

    public void RefreshValue()
    {
        //A bit of a cheat, but we provide a means to poke the Viewmodel
        //And raise a property change event
        RaisePropertyChanged("CurrentValue");
    }

这也是 SigChannel 模型:

 class SigGenChannel
{
    #region Private members
    private UtilityParticipant mParticipant;
    private double mCurrentValue;
    private double mFrequency;
    private double mAmplitude;
    private double mTarget;
    private double mOffset;
    private double mCurrentStepTime;
    private DateTime mStartTime;
    private System.Threading.Timer mTimer;
    private TimerCallback mTCallback;
    private int mUpdateInterval = 10;
    #endregion

    #region Public members
    public double CurrentValue
    {
        get
        {
            return mCurrentValue;
        }
        set
        {
            mCurrentValue = value;
        }
    }        

    public double Frequency
    {
        get
        {
            return mFrequency;
        }

        set
        {
            mFrequency = value;
        }
    }

    public double Amplitude
    {
        get
        {
            return mAmplitude;
        }
        set
        {
            mAmplitude = value;
        }
    }

    public double Target
    {
        get
        {
            return mTarget;
        }
        set
        {
            mTarget = value;
        }
    }
    #endregion


    //Constructor
    public SigGenChannel(UtilityParticipant aParticipant)
    {
        mParticipant = aParticipant;
        mCurrentValue = 10;
        mFrequency = 200;
        mAmplitude = 100;
        mOffset = 0;
        mCurrentStepTime = 0;
        mStartTime = DateTime.Now;
        mTCallback = this.Update;
        mTimer = new System.Threading.Timer(mTCallback, null, 500, mUpdateInterval);
        //Array enumData = Enum.GetNames;
        //RefreshItems();
        //Temp Code....!
        Collection lCollection = mParticipant.GetCollection("DefaultNodeName.NodeStats");
        lCollection.Publish();
    }

    private void Update(object StateInfo)
    {
        TimeSpan span = DateTime.Now - mStartTime;
        mCurrentStepTime = span.TotalMilliseconds / (double)1000;

        mCurrentValue = (Math.Sin(mCurrentStepTime * (mFrequency * 2 * Math.PI)) * mAmplitude / 2) + mOffset;

        //Temp Code...!
        Collection lCollection = mParticipant.GetCollection("DefaultNodeName.NodeStats");
        Parameter lParameter = lCollection.GetParameter("CPUPercent");
        lParameter.SetValue(mCurrentValue);
        lCollection.Send();

【问题讨论】:

    标签: wpf data-binding mvvm


    【解决方案1】:

    当前代码的编写方式不遵循 WPF 建议的做法,逆流而上会使事情变得比当时更难。

    代码应该做的是:

    为频道创建(视图)模型类

    例如:

    class ChannelModel
    {
        public int Value { get; set; }
        public int Frequency { get; set; }
    }
    

    使用ItemsControl 而不是StackPanel

    WPF 的做法是将控件绑定到集合,因此将 StackPanel 替换为 ItemsControl

    ItemsControl 绑定到模型的ObservableCollection

    您的主视图模型应该公开一个ObservableCollection&lt;ChannelModel&gt; 属性,并且控件应该直接绑定到该属性:

    <ItemsControl ItemsSource="{Binding CollectionOfChannelModels}"/>
    

    这可确保控件会自动更新您对集合所做的任何更改,而无需您执行任何其他操作。

    使用DataTemplate 指定每个模型的渲染方式

    到目前为止,我们已经获得了与您的频道集合保持同步的控件,但我们还需要告诉它应该如何显示每个项目(频道模型)。为此,请将DataTemplate 添加到ItemsControlResources 集合中:

    <ItemsControl.Resources>
      <DataTemplate DataType={x:Type local:ChannelModel}>
        <StackPanel Orientation="Horizontal">
          <TextBlock Text="Value" />
          <TextBox Text="{Binding Value}" />
        </StackPanel>
      </DataTemplate>
    </ItemsControl.Resources>
    

    【讨论】:

    • 您好,感谢您的回复,请看我编辑过的原帖
    • @Balaal:您应该可以正常使用当前的视图模型。
    【解决方案2】:

    通常的想法是为特定类型创建一个DataTemplate,在您的情况下它是为 ViewModel。

    为您的ViewModel 创建一个 DataTemplate,例如:

    <DataTemplate DataType={x:Type local:ViewModel}>
        <TextBox Text="{Binding ViewModelTextProperty}" />
    </DataTemplate>
    

    而且在您的 XAML 中,您还必须绑定您的 ViewModel 列表

    <ItemsControl ItemsSource="{Binding myListOfViewModels}"/>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-10-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-07
      相关资源
      最近更新 更多