DM-V-VM



using System;
using System.Collections.Generic;
using System.Text;

namespace ModelSample
{
    /// <summary>
    /// Interface for a provider of stock quotes.
    /// </summary>
    public interface IStockQuoteProvider
    {
        /// <summary>
        /// Get a quote. This function may block on slow operations like hitting the network.
        /// </summary>
        /// <param name="symbol">The stock symbol.</param>
        /// <param name="quote">The quote.</param>
        /// <returns>Whether we were able to get a quote.</returns>
        bool TryGetQuote(string symbol, out double quote);
    }
}

using System;
using System.Collections.Generic;
using System.Text;

namespace ModelSample
{
    /// <summary>
    /// Mock IStockQuoteProvider that alwyas returns 100.
    /// </summary>
    public class MockQuoteProvider : IStockQuoteProvider
    {
        public bool TryGetQuote(string symbol, out double quote)
        {
            quote = 100.0;

            return true;
        }
    }
}

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Windows.Input;

namespace ModelSample
{
    /// <summary>
    /// View model for the portfolio view
    /// </summary>
    public class PortfolioViewModel
    {
        public PortfolioViewModel(IStockQuoteProvider quoteProvider)
        {
            _quoteProvider = quoteProvider;
            _stockModels = new ObservableCollection<StockModel>();
            _stockModels.Add(new StockModel("MSFT", _quoteProvider));

            _addCommand = new AddCommand(this);
            _removeCommand = new RemoveCommand(this);
        }

        /// <summary>
        /// The list of StockModels for the page.
        /// </summary>
        public ObservableCollection<StockModel> Stocks
        {
            get { return _stockModels; }
        }

        /// <summary>
        /// A CommandModel for Add. The command parameter should be the symbol to add.
        /// </summary>
        public CommandModel AddCommandModel
        {
            get { return _addCommand; }
        }

        /// <summary>
        /// A CommandModel for Remove. The command parameter should be the StockModel to remove.
        /// </summary>
        public CommandModel RemoveCommandModel
        {
            get { return _removeCommand; }
        }

        /// <summary>
        /// Private implementation of the Add Command
        /// </summary>
        private class AddCommand : CommandModel
        {
            public AddCommand(PortfolioViewModel viewModel)
            {
                _viewModel = viewModel;
            }

            public override void OnQueryEnabled(object sender, CanExecuteRoutedEventArgs e)
            {
                string symbol = e.Parameter as string;
                e.CanExecute = (!string.IsNullOrEmpty(symbol));
                e.Handled = true;
            }

            public override void OnExecute(object sender, ExecutedRoutedEventArgs e)
            {
                string symbol = e.Parameter as string;
                _viewModel._stockModels.Add(new StockModel(symbol, _viewModel._quoteProvider));    
            }

            private PortfolioViewModel _viewModel;
        }

        /// <summary>
        /// Private implementation of the Remove command
        /// </summary>
        private class RemoveCommand : CommandModel
        {
            public RemoveCommand(PortfolioViewModel viewModel)
            {
                _viewModel = viewModel;
            }

            public override void OnQueryEnabled(object sender, CanExecuteRoutedEventArgs e)
            {
                e.CanExecute = e.Parameter is StockModel;
                e.Handled = true;
            }

            public override void OnExecute(object sender, ExecutedRoutedEventArgs e)
            {
                _viewModel._stockModels.Remove(e.Parameter as StockModel);
            }

            private PortfolioViewModel _viewModel;
        }

        private ObservableCollection<StockModel> _stockModels;
        private CommandModel _addCommand;
        private CommandModel _removeCommand;
        private IStockQuoteProvider _quoteProvider;
    }
}

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Threading;
using System.Windows.Threading;

namespace ModelSample
{
    public class StockModel : DataModel
    {
        public StockModel(string symbol, IStockQuoteProvider quoteProvider)
        {
            _symbol = symbol;
            _quoteProvider = quoteProvider;
        }

        protected override void OnActivated()
        {
            VerifyCalledOnUIThread();

            base.OnActivated();

            _timer = new DispatcherTimer(DispatcherPriority.Background);
            _timer.Interval = TimeSpan.FromMinutes(5);
            _timer.Tick += delegate { ScheduleUpdate(); };
            _timer.Start();

            ScheduleUpdate();
        }

        protected override void OnDeactivated()
        {
            VerifyCalledOnUIThread();

            base.OnDeactivated();

            _timer.Stop();
            _timer = null;
        }

        private void ScheduleUpdate()
        {
            VerifyCalledOnUIThread();

            // Queue a work item to fetch the quote
            if (ThreadPool.QueueUserWorkItem(new WaitCallback(FetchQuoteCallback)))
            {
                this.State = ModelState.Fetching;
            }
        }

        /// <summary>
        /// Gets the stock symbol.
        /// </summary>
        public string Symbol
        {
            get { return _symbol; }
        }

        /// <summary>
        /// Gets the current quote for the stock. Only valid if State == Active.
        /// </summary>
        public double Quote
        {
            get
            {
                VerifyCalledOnUIThread();

                return _quote;
            }

            private set
            {
                VerifyCalledOnUIThread();

                if (_quote != value)
                {
                    _quote = value;
                    SendPropertyChanged("Quote");
                }
            }
        }

        /// <summary>
        /// Callback on background thread to fecth quote.
        /// </summary>
        private void FetchQuoteCallback(object state)
        {
            double fetchedQuote;
            if (_quoteProvider.TryGetQuote(_symbol, out fetchedQuote))
            {
                this.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new ThreadStart(delegate
                    {
                        this.Quote = fetchedQuote;
                        this.State = ModelState.Valid;
                    }));
            }
            else
            {
                this.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new ThreadStart(delegate
                    { this.State = ModelState.Invalid; }));
            }
        }

        private string _symbol;
        private double _quote;
        private IStockQuoteProvider _quoteProvider;
        private DispatcherTimer _timer;
    }
}

<Window x:Class="ModelSample.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="ModelSample" Height="300" Width="300"
    >
    <Grid>
      <ContentControl x:Name="_content" />
    </Grid>
</Window>



using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;


namespace ModelSample
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>

    public partial class Window1 : Window
    {

        public Window1()
        {
            InitializeComponent();

            _content.Content = new PortfolioViewModel(new MockQuoteProvider());
        }

    }
}


<Application x:Class="ModelSample.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:ModelSample" 
    StartupUri="Window1.xaml"
    >
  <Application.Resources>
    <!-- Data Template for individual stocks -->
    <DataTemplate DataType="{x:Type local:StockModel}">
      <StackPanel Orientation="Horizontal" local:ActivateModel.Model="{Binding}">
        <TextBlock Text="{Binding Symbol}" Width="100"/>
        <TextBlock Text="{Binding Quote}" />
      </StackPanel>
    </DataTemplate>

    <!-- Definition of Portfolio view -->
    <DataTemplate DataType="{x:Type local:PortfolioViewModel}">
      <DockPanel LastChildFill="True">
        <TextBlock DockPanel.Dock="Top" TextAlignment="Center">Portfolio View</TextBlock>
        <StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
          <TextBox Name="AddSymbol" Width="100" />
          <Button Command="{Binding AddCommandModel.Command}"
                  CommandParameter="{Binding Path=Text,ElementName=AddSymbol}"
                  local:CreateCommandBinding.Command="{Binding AddCommandModel}"
                  >
            Add
            </Button>
          <Button Margin="50,0,0,0"
                  Command="{Binding RemoveCommandModel.Command}"
                  CommandParameter="{Binding Path=SelectedItem,ElementName=StockList}"
                  local:CreateCommandBinding.Command="{Binding RemoveCommandModel}"
                  >
            Remove
          </Button>
        </StackPanel>
        <ListBox Name="StockList" ItemsSource="{Binding Stocks}" />
      </DockPanel>
    </DataTemplate>
  </Application.Resources>
</Application>



using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Input;

namespace ModelSample
{
    /// <summary>
    /// Model for a command
    /// </summary>
    public abstract class CommandModel
    {
        public CommandModel()
        {
            _routedCommand = new RoutedCommand();
        }

        /// <summary>
        /// Routed command associated with the model.
        /// </summary>
        public RoutedCommand Command
        {
            get { return _routedCommand; }
        }

        /// <summary>
        /// Determines if a command is enabled. Override to provide custom behavior. Do not call the
        /// base version when overriding.
        /// </summary>
        public virtual void OnQueryEnabled(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
            e.Handled = true;
        }

        /// <summary>
        /// Function to execute the command.
        /// </summary>
        public abstract void OnExecute(object sender, ExecutedRoutedEventArgs e);

        private RoutedCommand _routedCommand;
    }
}

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Input;

namespace ModelSample
{
    /// <summary>
    /// Attached property that can be used to create a binding for a CommandModel. Set the
    /// CreateCommandBinding.Command property to a CommandModel.
    /// </summary>
    public static class CreateCommandBinding
    {
        public static readonly DependencyProperty CommandProperty
           = DependencyProperty.RegisterAttached("Command", typeof(CommandModel), typeof(CreateCommandBinding),
                new PropertyMetadata(new PropertyChangedCallback(OnCommandInvalidated)));

        public static CommandModel GetCommand(DependencyObject sender)
        {
            return (CommandModel)sender.GetValue(CommandProperty);
        }

        public static void SetCommand(DependencyObject sender, CommandModel command)
        {
            sender.SetValue(CommandProperty, command);
        }

        /// <summary>
        /// Callback when the Command property is set or changed.
        /// </summary>
        private static void OnCommandInvalidated(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            // Clear the exisiting bindings on the element we are attached to.
            UIElement element = (UIElement)dependencyObject;
            element.CommandBindings.Clear();

            // If we're given a command model, set up a binding
            CommandModel commandModel = e.NewValue as CommandModel;
            if (commandModel != null)
            {
                element.CommandBindings.Add(new CommandBinding(commandModel.Command, commandModel.OnExecute, commandModel.OnQueryEnabled));
            }

            // Suggest to WPF to refresh commands
            CommandManager.InvalidateRequerySuggested();
        }

    }
}

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows.Threading;

namespace ModelSample
{
    /// <summary>
    /// Base class for data models. All public methods must be called on the UI thread only.
    /// </summary>
    public class DataModel : INotifyPropertyChanged
    {
        public DataModel()
        {
            _dispatcher = Dispatcher.CurrentDispatcher;
            this.State = ModelState.Invalid;
        }

        /// <summary>
        /// Possible states for a DataModel.
        /// </summary>
        public enum ModelState
        {
            Invalid,    // The model is in an invalid state
            Fetching,   // The model is being fetched
            Valid       // The model has fetched its data
        }

        /// <summary>
        /// Is the model active?
        /// </summary>
        public bool IsActive
        {
            get
            {
                VerifyCalledOnUIThread();
                return _isActive;
            }

            private set
            {
                VerifyCalledOnUIThread();
                if (value != _isActive)
                {
                    _isActive = value;
                    SendPropertyChanged("IsActive");
                }
            }
        }

        /// <summary>
        /// Activate the model.
        /// </summary>
        public void Activate()
        {
            VerifyCalledOnUIThread();

            if (!_isActive)
            {
                this.IsActive = true;
                OnActivated();
            }
        }

        /// <summary>
        /// Override to provide behavior on activate.
        /// </summary>
        protected virtual void OnActivated()
        {
        }

        /// <summary>
        /// Deactivate the model.
        /// </summary>
        public void Deactivate()
        {
            VerifyCalledOnUIThread();

            if (_isActive)
            {
                this.IsActive = false;
                OnDeactivated();
            }
        }

        /// <summary>
        /// Override to provide behavior on deactivate.
        /// </summary>
        protected virtual void OnDeactivated()
        {
        }

        /// <summary>
        /// PropertyChanged event for INotifyPropertyChanged implementation.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged
        {
            add
            {
                VerifyCalledOnUIThread();
                _propertyChangedEvent += value;
            }
            remove
            {
                VerifyCalledOnUIThread();
                _propertyChangedEvent -= value;
            }
        }

        /// <summary>
        /// Gets or sets current state of the model.
        /// </summary>
        public ModelState State
        {
            get
            {
                VerifyCalledOnUIThread();
                return _state;
            }

            set
            {
                VerifyCalledOnUIThread();
                if (value != _state)
                {
                    _state = value;
                    SendPropertyChanged("State");
                }
            }
        }

        /// <summary>
        /// The Dispatcher associated with the model.
        /// </summary>
        public Dispatcher Dispatcher
        {
            get { return _dispatcher; }
        }

        /// <summary>
        /// Utility function for use by subclasses to notify that a property has changed.
        /// </summary>
        /// <param name="propertyName">The name of the property.</param>
        protected void SendPropertyChanged(string propertyName)
        {
            VerifyCalledOnUIThread();
            if (_propertyChangedEvent != null)
            {
                _propertyChangedEvent(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        /// <summary>
        /// Debugging utility to make sure functions are called on the UI thread.
        /// </summary>
        [Conditional("Debug")]
        protected void VerifyCalledOnUIThread()
        {
            Debug.Assert(Dispatcher.CurrentDispatcher == this.Dispatcher, "Call must be made on UI thread.");
        }

        private ModelState _state;
        private Dispatcher _dispatcher;
        private PropertyChangedEventHandler _propertyChangedEvent;
        private bool _isActive;
    }
}


using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Windows;

namespace ModelSample
{
    /// <summary>
    /// Attached property that can be used to activate a model.
    /// </summary>
    public static class ActivateModel
    {
        public static readonly DependencyProperty ModelProperty
           = DependencyProperty.RegisterAttached("Model", typeof(DataModel), typeof(ActivateModel),
                new PropertyMetadata(new PropertyChangedCallback(OnModelInvalidated)));

        public static DataModel GetModel(DependencyObject sender)
        {
            return (DataModel)sender.GetValue(ModelProperty);
        }

        public static void SetModel(DependencyObject sender, DataModel model)
        {
            sender.SetValue(ModelProperty, model);
        }

        /// <summary>
        /// Callback when the Model property is set or changed.
        /// </summary>
        private static void OnModelInvalidated(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            FrameworkElement element = (FrameworkElement)dependencyObject;

            // Add handlers if necessary
            if (e.OldValue == null && e.NewValue != null)
            {
                element.Loaded += OnElementLoaded;
                element.Unloaded += OnElementUnloaded;
            }

            // Or, remove if necessary
            if (e.OldValue != null && e.NewValue == null)
            {
                element.Loaded -= OnElementLoaded;
                element.Unloaded -= OnElementUnloaded;
            }

            // If loaded, deactivate old model and activate new one
            if (element.IsLoaded)
            {
                if (e.OldValue != null)
                {
                    ((DataModel)e.OldValue).Deactivate();
                }

                if (e.NewValue != null)
                {
                    ((DataModel)e.NewValue).Activate();
                }
            }
        }
 
        /// <summary>
        /// Activate the model when the element is loaded.
        /// </summary>
        static void OnElementLoaded(object sender, RoutedEventArgs e)
        {
            FrameworkElement element = (FrameworkElement)sender;
            DataModel model = GetModel(element);
            model.Activate();
        }

        /// <summary>
        /// Deactivate the model when the element is unloaded.
        /// </summary>
        static void OnElementUnloaded(object sender, RoutedEventArgs e)
        {
            FrameworkElement element = (FrameworkElement)sender;
            DataModel model = GetModel(element);
            model.Deactivate();
        }

    }
}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值