在 View-Model 關閉一個在View裡的視窗

  • 1975
  • 0
  • 2013-11-21

摘要:用相依性屬性 Attach 到視窗上,給 View-Model 關閉它

用相依性屬性 Attach 到視窗上,給 View-Model 關閉它
 
先假設寫一個用 RegisterAttach 註冊的相依性屬性,放到 Window 表頭上,這樣子 View-Model 就可以抓到這個 Window,在想關掉它的時候呼叫它的 Close().
但不巧的是,實際上 Run 的時候,執行完 Close() 它會出現錯誤:
 
「呼叫執行緒無法存取此物件,因為此物件屬於另一個執行緒。」
 
現在變成要用執行緒的方法解決這個問題。=>這是因為在 DependencyProperty中存的是Window,因為是static的,所以關閉的時候並沒有被release掉,才會造成這個問題。
只要把存Window Reference 改成存一個 Delegate 的話,就不會有這個問題了。
請看..
 
1、假設我的attach class叫MyAttachClass,在class中用DependencyProperty.RegisterAttached的方式註冊一個DependencyProperty:

namespace MyNameSpace
{
public class MyAttachClass
{
#region 相依性屬性 DependencyProperty: MyWindowActionProperty
public delegate void DCloseSubWindow();
public static DCloseSubWindow MyCloseSubWindow;
static void MyWindowActionChanged(DependencyObject Owner, DependencyPropertyChangedEventArgs e)
{
  if (Owner != null)
  {
    Window win = Owner as Window;
    if (win != null)
    {
      MyCloseSubWindow = new DCloseSubWindow(win.Close);
    }
  }
}
public static DependencyProperty MyWindowActionProperty = DependencyProperty.RegisterAttached(
    "MyWindowAction",
    typeof(string),
    typeof(MyClass),
    new FrameworkPropertyMetadata(
        new PropertyChangedCallback(MyWindowActionChanged)));
public static void SetMyWindowAction(DependencyObject t, string value)
{
  t.SetValue(MyWindowActionProperty, value);
}
public static string GetMyWindowAction(DependencyObject t)
{
  return (string) t.GetValue(MyWindowActionProperty);
}
#endregion //相依性屬性 DependencyProperty: MyWindowActionProperty
}

...
}

 

2、在子window表頭上引用此物件,並加入DependencyProperty,指定什麼值並沒影響,因為我們只要Attach上去就行了。(子window的DataContent要指到View-Model)


<Window x:Class="MyNameSpace.MySubWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:a="clr-namespace:MyNameSpace;assembly=MyNameSpace"
  a:MyAttachClass.MyWindowAction=""
>
<Grid>
  <Button Command="{Binding CmdCloseMe}">Close Me!</Button>
</Grid>
</Window>

3、在View-Model中宣告ICommand,並在Execute中呼叫關閉子window的Delegate。


namespace MyNameSpace
{

  ...
  public class MyClass
  {
    public RelayCommand CmdCloseMe { get; set; }

    public MyClass()
    {
    CmdCloseMe = new RelayCommand(a => CloseMe());
    }

    void CloseMe()
    {
      //在這裡呼叫下面的delegate就可以關掉子window了
      MyNameSpace.MyAttachClass.MyCloseSubWindow();
    }
  }

  public class RelayCommand : ICommand
  {
    readonly Action_execute; 
    public RelayCommand(ActionExecute) 
    {
      _execute = Execute;
    } 
    public override void Execute(object parameter)
    {
      _execute(parameter);
    }
    public bool CanExecute(object parameter)
    {
      return true;
    }
    public event EventHandler CanExecuteChanged;
   }
  }