나는이 Button
그것을 클릭 할 때 내 방 창문을 닫고 그 :
<Button x:Name="buttonOk" IsCancel="True">Ok</Button>
ie 에 a Command
를 추가 할 때까지 괜찮습니다.Button
<Button x:Name="buttonOk"
Command="{Binding SaveCommand}"
IsCancel="True">Ok</Button>
이제는 Command
. 입력 EventHandler
하고 this.Close()
ie를 호출 하여이 문제를 해결할 수 있습니다.
<Button x:Name="buttonOk"
Click="closeWindow"
Command="{Binding SaveCommand}"
IsCancel="True">Ok</Button>
하지만 지금은 내 코드에 코드가 SaveCommand
있습니다. MVVM 패턴을 사용하고 SaveCommand
있으며 내 코드에서 유일한 코드입니다.
뒤에서 코드를 사용하지 않도록 어떻게 다르게 할 수 있습니까?
답변
이 주제에 대한 블로그 게시물 을 방금 완료했습니다 . 간단히 말해서 및 접근 Action
자를 사용하여 ViewModel에 속성을 추가 하십시오 . 그런 다음 생성자 에서 정의하십시오 . 마지막으로 창을 닫아야하는 바인딩 된 명령에서 작업을 호출합니다.get
set
Action
View
ViewModel에서 :
public Action CloseAction { get; set;}
그리고 View
생성자에서 :
private View()
{
InitializeComponent();
ViewModel vm = new ViewModel();
this.DataContext = vm;
if ( vm.CloseAction == null )
vm.CloseAction = new Action(this.Close);
}
마지막으로 창을 닫아야하는 바인딩 된 명령이 무엇이든 간단히 호출 할 수 있습니다.
CloseAction(); // Calls Close() method of the View
이것은 나를 위해 일했고 상당히 우아한 솔루션처럼 보였으며 많은 코딩을 절약했습니다.
답변
매우 깨끗하고 MVVM 방식은 사용 InteractionTrigger
하고 CallMethodAction
정의하는 것입니다.Microsoft.Interactivity.Core
아래와 같이 새 네임 스페이스를 추가해야합니다.
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
Microsoft.Xmal.Behaviours.Wpf 어셈블리 가 필요하면 아래 xaml 코드가 작동합니다.
<Button Content="Save" Command="{Binding SaveCommand}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:CallMethodAction MethodName="Close"
TargetObject="{Binding RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType=Window}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
뒤에 코드 나 다른 코드가 필요하지 않으며의 다른 메서드를 호출 할 수도 있습니다 Window
.
답변
누군가가 언급했듯이 내가 게시 한 코드는 MVVM 친화적이지 않습니다. 두 번째 솔루션은 어떻습니까?
1st, not MVVM 솔루션 (참고로 삭제하지 않겠습니다)
XAML :
<Button Name="okButton" Command="{Binding OkCommand}" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">OK</Button>
ViewModel :
public ICommand OkCommand
{
get
{
if (_okCommand == null)
{
_okCommand = new ActionCommand<Window>(DoOk, CanDoOk);
}
return _okCommand ;
}
}
void DoOk(Window win)
{
// Your Code
win.DialogResult = true;
win.Close();
}
bool CanDoOk(Window win) { return true; }
두 번째, 아마도 더 나은 솔루션 :
연결된 동작 사용
XAML
<Button Content="Ok and Close" Command="{Binding OkCommand}" b:CloseOnClickBehaviour.IsEnabled="True" />
모델보기
public ICommand OkCommand
{
get { return _okCommand; }
}
Behavior Class 다음과 유사한 것 :
public static class CloseOnClickBehaviour
{
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.RegisterAttached(
"IsEnabled",
typeof(bool),
typeof(CloseOnClickBehaviour),
new PropertyMetadata(false, OnIsEnabledPropertyChanged)
);
public static bool GetIsEnabled(DependencyObject obj)
{
var val = obj.GetValue(IsEnabledProperty);
return (bool)val;
}
public static void SetIsEnabled(DependencyObject obj, bool value)
{
obj.SetValue(IsEnabledProperty, value);
}
static void OnIsEnabledPropertyChanged(DependencyObject dpo, DependencyPropertyChangedEventArgs args)
{
var button = dpo as Button;
if (button == null)
return;
var oldValue = (bool)args.OldValue;
var newValue = (bool)args.NewValue;
if (!oldValue && newValue)
{
button.Click += OnClick;
}
else if (oldValue && !newValue)
{
button.PreviewMouseLeftButtonDown -= OnClick;
}
}
static void OnClick(object sender, RoutedEventArgs e)
{
var button = sender as Button;
if (button == null)
return;
var win = Window.GetWindow(button);
if (win == null)
return;
win.Close();
}
}
답변
나는 개인적으로 이런 종류의 일을하기 위해 행동을 사용합니다.
public class WindowCloseBehaviour : Behavior<Window>
{
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register(
"Command",
typeof(ICommand),
typeof(WindowCloseBehaviour));
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.Register(
"CommandParameter",
typeof(object),
typeof(WindowCloseBehaviour));
public static readonly DependencyProperty CloseButtonProperty =
DependencyProperty.Register(
"CloseButton",
typeof(Button),
typeof(WindowCloseBehaviour),
new FrameworkPropertyMetadata(null, OnButtonChanged));
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public object CommandParameter
{
get { return GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
public Button CloseButton
{
get { return (Button)GetValue(CloseButtonProperty); }
set { SetValue(CloseButtonProperty, value); }
}
private static void OnButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var window = (Window)((WindowCloseBehaviour)d).AssociatedObject;
((Button) e.NewValue).Click +=
(s, e1) =>
{
var command = ((WindowCloseBehaviour)d).Command;
var commandParameter = ((WindowCloseBehaviour)d).CommandParameter;
if (command != null)
{
command.Execute(commandParameter);
}
window.Close();
};
}
}
그런 다음에이를 첨부 할 수 있습니다 Window
및 Button
작업을 할 :
<Window x:Class="WpfApplication6.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:local="clr-namespace:WpfApplication6"
Title="Window1" Height="300" Width="300">
<i:Interaction.Behaviors>
<local:WindowCloseBehaviour CloseButton="{Binding ElementName=closeButton}"/>
</i:Interaction.Behaviors>
<Grid>
<Button Name="closeButton">Close</Button>
</Grid>
</Window>
닫기 전에 명령을 실행할 수 있도록 Command
및 CommandParameter
여기에 추가했습니다 Window
.
답변
작은 앱의 경우 창과 DataContext를 표시, 닫기 및 처리하기 위해 자체 애플리케이션 컨트롤러를 사용합니다. 애플리케이션 UI의 중심점입니다.
다음과 같습니다.
//It is singleton, I will just post 2 methods and their invocations
public void ShowNewWindow(Window window, object dataContext = null, bool dialog = true)
{
window.DataContext = dataContext;
addToWindowRegistry(dataContext, window);
if (dialog)
window.ShowDialog();
else
window.Show();
}
public void CloseWindow(object dataContextSender)
{
var correspondingWindows = windowRegistry.Where(c => c.DataContext.Equals(dataContextSender)).ToList();
foreach (var pair in correspondingWindows)
{
pair.Window.Close();
}
}
및 ViewModels 에서의 호출 :
// Show new Window with DataContext
ApplicationController.Instance.ShowNewWindow(
new ClientCardsWindow(),
new ClientCardsVM(),
false);
// Close Current Window from viewModel
ApplicationController.Instance.CloseWindow(this);
물론 내 솔루션에서 몇 가지 제한 사항을 찾을 수 있습니다. 다시 말하지만 저는 작은 프로젝트에 사용하고 충분합니다. 관심이 있으시면 여기 또는 다른 곳에 전체 코드를 게시 할 수 있습니다.
답변
이 문제를 일반적인 MVVM 방식으로 해결하려고 시도했지만 항상 불필요한 복잡한 논리로 끝납니다. 밀접한 동작을 달성하기 위해 코드 뒤에 코드가 없다는 규칙에서 예외를 만들고 코드 뒤에 좋은 이벤트를 사용했습니다.
XAML :
<Button Content="Close" Click="OnCloseClicked" />
뒤에있는 코드 :
private void OnCloseClicked(object sender, EventArgs e)
{
Visibility = Visibility.Collapsed;
}
명령 / MVVM을 사용하여 이것이 더 잘 지원되기를 원하지만 이벤트를 사용하는 것보다 더 간단하고 명확한 솔루션은 없다고 생각합니다.
답변
복잡한 클래스 종속성에 대해 게시 구독 패턴 을 사용합니다 .
ViewModel :
public class ViewModel : ViewModelBase
{
public ViewModel()
{
CloseComand = new DelegateCommand((obj) =>
{
MessageBus.Instance.Publish(Messages.REQUEST_DEPLOYMENT_SETTINGS_CLOSED, null);
});
}
}
창문:
public partial class SomeWindow : Window
{
Subscription _subscription = new Subscription();
public SomeWindow()
{
InitializeComponent();
_subscription.Subscribe(Messages.REQUEST_DEPLOYMENT_SETTINGS_CLOSED, obj =>
{
this.Close();
});
}
}
Bizmonger 를 활용할 수 있습니다. 를 활용하여 MessageBus를 얻을 .
MessageBus
public class MessageBus
{
#region Singleton
static MessageBus _messageBus = null;
private MessageBus() { }
public static MessageBus Instance
{
get
{
if (_messageBus == null)
{
_messageBus = new MessageBus();
}
return _messageBus;
}
}
#endregion
#region Members
List<Observer> _observers = new List<Observer>();
List<Observer> _oneTimeObservers = new List<Observer>();
List<Observer> _waitingSubscribers = new List<Observer>();
List<Observer> _waitingUnsubscribers = new List<Observer>();
int _publishingCount = 0;
#endregion
public void Subscribe(string message, Action<object> response)
{
Subscribe(message, response, _observers);
}
public void SubscribeFirstPublication(string message, Action<object> response)
{
Subscribe(message, response, _oneTimeObservers);
}
public int Unsubscribe(string message, Action<object> response)
{
var observers = new List<Observer>(_observers.Where(o => o.Respond == response).ToList());
observers.AddRange(_waitingSubscribers.Where(o => o.Respond == response));
observers.AddRange(_oneTimeObservers.Where(o => o.Respond == response));
if (_publishingCount == 0)
{
observers.ForEach(o => _observers.Remove(o));
}
else
{
_waitingUnsubscribers.AddRange(observers);
}
return observers.Count;
}
public int Unsubscribe(string subscription)
{
var observers = new List<Observer>(_observers.Where(o => o.Subscription == subscription).ToList());
observers.AddRange(_waitingSubscribers.Where(o => o.Subscription == subscription));
observers.AddRange(_oneTimeObservers.Where(o => o.Subscription == subscription));
if (_publishingCount == 0)
{
observers.ForEach(o => _observers.Remove(o));
}
else
{
_waitingUnsubscribers.AddRange(observers);
}
return observers.Count;
}
public void Publish(string message, object payload)
{
_publishingCount++;
Publish(_observers, message, payload);
Publish(_oneTimeObservers, message, payload);
Publish(_waitingSubscribers, message, payload);
_oneTimeObservers.RemoveAll(o => o.Subscription == message);
_waitingUnsubscribers.Clear();
_publishingCount--;
}
private void Publish(List<Observer> observers, string message, object payload)
{
Debug.Assert(_publishingCount >= 0);
var subscribers = observers.Where(o => o.Subscription.ToLower() == message.ToLower());
foreach (var subscriber in subscribers)
{
subscriber.Respond(payload);
}
}
public IEnumerable<Observer> GetObservers(string subscription)
{
var observers = new List<Observer>(_observers.Where(o => o.Subscription == subscription));
return observers;
}
public void Clear()
{
_observers.Clear();
_oneTimeObservers.Clear();
}
#region Helpers
private void Subscribe(string message, Action<object> response, List<Observer> observers)
{
Debug.Assert(_publishingCount >= 0);
var observer = new Observer() { Subscription = message, Respond = response };
if (_publishingCount == 0)
{
observers.Add(observer);
}
else
{
_waitingSubscribers.Add(observer);
}
}
#endregion
}
}
신청
public class Subscription
{
#region Members
List<Observer> _observerList = new List<Observer>();
#endregion
public void Unsubscribe(string subscription)
{
var observers = _observerList.Where(o => o.Subscription == subscription);
foreach (var observer in observers)
{
MessageBus.Instance.Unsubscribe(observer.Subscription, observer.Respond);
}
_observerList.Where(o => o.Subscription == subscription).ToList().ForEach(o => _observerList.Remove(o));
}
public void Subscribe(string subscription, Action<object> response)
{
MessageBus.Instance.Subscribe(subscription, response);
_observerList.Add(new Observer() { Subscription = subscription, Respond = response });
}
public void SubscribeFirstPublication(string subscription, Action<object> response)
{
MessageBus.Instance.SubscribeFirstPublication(subscription, response);
}
}