2010-03-30 5 views
1

내 WPF 응용 프로그램에는 항목이있는 목록 상자가 있습니다. 목록 상자는 XAML의 xmldataprovider를 통해 채워진 다음 목록 상자의 Itemssource 속성에 바인딩됩니다.프로그래밍 방식으로 WPF의 목록 상자에 명령 추가

음은 XAML에서, 나는 수행하여 목록 상자에 COMAND 바인딩 :

     <ListBox.CommandBindings> 
          <CommandBinding 
           Command="{x:Static local:mainApp.MyCmd}" 
           CanExecute="CanExecute" 
           Executed ="Executed" /> 
         </ListBox.CommandBindings> 

하지만 난 프로그래밍 방식으로 각 ListBoxItem의에 명령을 결합하는 방법을 모르겠어요. 그것을하는 방법?

미리 감사드립니다.


처음으로 의견을 보내지 않아서 죄송합니다. 이 모든 것을 주석에 넣을 수는 없습니다.

좋아요, 예 제가 사용자 지정 클래스에 등록하고 구현했지만 ICommandSource의 Executed 및 CanExecute 속성을 사용하고 있지 않습니다 (xaml에서도 주석이 달렸습니다). 나는 routedCommand에서 그들을 지정했지만 사용자 정의 클래스에서,이 수행하여 윈도우의 생성자에서 불가능했을 :

뒤에

의 WinMain 코드 :

public WinMain() 
{ 
    InitializeComponent(); 

    // Command binding. If I don't do this Executed and CanExecute are not executed 
    CommandBindings.Add(new CommandBinding(rcmd, 
     CommandBinding_Executed, CommandBinding_CanExecute)); 
} 

후 나는의 WinMain 코드에서이 메소드를 구현을 뒤에 그것을 너무 같이

<Classes:CommandListBox x:Name="LayoutListBox" 
Command="{x:Static local:WinMain.rcmd}" 
    ... > 

<...> 

</Classes:CommandListBox> 
:
// ExecutedRoutedEventHandler 
private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e) 
{ 
    // Do stuff 

} 

// CanExecuteRoutedEventHandler 
private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e) 
{ 

    // cBgWorkers is a class that check if a background worker is running 
    e.CanExecute = !cBgWorkers.isRunning; 

    //if (LayoutListBox != null) LayoutListBox.IsEnabled = !cBgWorkers.isRunning; 
} 

과의 WinMain XAML에서

는이 같은 명령을 호출

그리고 내 사용자 정의 클래스 CommandListBox에 난 당신이 내가 사용 또는 배경 근로자가 완료 여부에 따라 제어 비활성화 볼 수있는 CanExecuteChanged 있습니다

private void CanExecuteChanged(object sender, EventArgs e) 
{ 
    this.Enabled = !cBgWorkers.isRunning; 
} 

하지만 사용자 정의 클래스에서 내가 구현하지 않은를 당신이 말하는 이벤트 핸들러, OnSelected.

모든 작업이 정상적으로 수행되고 사용자 지정 컨트롤 호출 명령 및 CanExecute 메서드에 도달하고 CanExecute는 백그라운드 작업자가 완료되었는지에 따라 올바른 값을 true 또는 false로 가져오고 CanExecuteChanged는 사용자 지정 컨트롤에서 발생합니다. CanExecute는 값을 변경합니다. 백그라운드 작업자가 시작되면 사용 중지되지만 사용 완료되면 사용 불가능하게됩니다. 나는 디버깅을했고 배경 작업자가 끝나면 CanExecuteChanged가 실행되고 this.Enabled가 올바른 값을 얻고 있음을 알 수있다. 그러나 UI의 어떤 이유로 UI 컨트롤은 올바른 값을 얻었음에도 불구하고 컨트롤이 계속 실행되고 RunWOrkerCompleted (배경 worker) CommandManager.InvalidateRequerySuggested()로 UI를 강제 업데이트합니다.

I는 주석 처리를 행함으로써이 문제를 해결!

경우 (! LayoutListBox = NULL) LayoutListBox.IsEnabled = cBgWorkers.isRunning;

CanExecute 메서드입니다. 나는 무슨 일이 일어나는 지 이해하지 못한다. 나는 당신이 그것을 할 필요가 없습니다 무슨 말을 할 그런 경우

:

CommandBindings.Add(new CommandBinding(rcmd, 
     CommandBinding_Executed, CommandBinding_CanExecute)); 

및 CommandBinding_Executed & CommandBinding_CanExecute 구현. 내가 맞습니까?

그러나이 방법을 제거하면 어디에서 this.enabled =! cBgWorkers.isRunning을 설정할 수 있습니까?

WPF에서 내 사용자 지정 컨트롤의 isEnabled 속성을 자동으로 설정하고 싶습니다. 이 작업을 수행하는 방법?

미리 감사드립니다.


첨부 된 비헤이비어에 대한 기사를 적용하여 몇 가지 변경 사항을 적용하여 내 ListBox에 적용합니다. 그것은 잘 작동하지 않거나 아마도 내가 잘못된 것을하고 있습니다. 내가 원하는 것은 긴 작업 (백그라운드 작업자)이 실행될 때 ListBox 멤버 (listBoxItems)를 선택할 수 없도록하는 것입니다. 그래서 내가 수정 한 문서의 방법 중 하나입니다

static void OnListBoxItemSelected(object sender, RoutedEventArgs e) 
    { 
     // Only react to the Selected event raised by the ListBoxItem 
     // whose IsSelected property was modified. Ignore all ancestors 
     // who are merely reporting that a descendant's Selected fired. 
     if (!Object.ReferenceEquals(sender, e.OriginalSource)) 
      return; 

     ListBoxItem item = e.OriginalSource as ListBoxItem; 
     if (item != null) 
     { 

      // (*) See comment under 
      item.IsEnabled = !cBgWorkers.isRunning; 
      if (!cBgWorkers.isRunning) 
      { 
       item.BringIntoView(); 
      } 
     } 
    } 

(*) cBgWorkers는 몇 가지 방법과 속성을 가진 공공 정적 클래스입니다. 현재 백그라운드 작업자가 없음을 나타내는 isRunning 속성 중 하나입니다. 다음 백그라운드 작업자가 실행되고 있지 않으면 목록 상자 멤버를 사용하도록 설정해야합니다. 그렇지 않으면 사용자가 하나의 목록 상자 항목을 클릭 할 때 비활성화해야하기 때문에 현재 페이지가 다른 페이지로 변경되지 않습니다. 내 주요 응용 프로그램에서 페이지).

백그라운드 작업자 (bw) 또는 모두가 실행 중이고 listbox 항목을 모두 선택하면 ok입니다. bw가 실행되고 현재 페이지를 다른 페이지로 변경하지 않기 때문에 목록 상자 항목이 비활성화됩니다. 물론, 목록 상자 항목 (또는 목록 상자 항목)을 사용하지 않도록 설정 한 경우 비활성화 되었기 때문에 다시 선택할 수 없습니다. 문제는 BW가 실행되는 동안 비활성화 된 목록 상자 항목을 bw로 끝내기를 원하기 때문입니다. 그들은 다시 활성화됩니다. 불행히도 첨부 된 동작은 WPF에 의해 자동으로 수행되지 않고 명령에 WPF에 의해 자동으로 업데이트되는 이점이 있습니다. 그래서, bw가 실행 중이거나 각각 실행되지 않을 때 listbox 항목을 비활성화/다시 활성화하는 방법은 무엇입니까?

내가 알고있는 한, 붙어있는 행동의 한 가지 이점은 행동을 끊임없이 호출하지 않기 때문에 (행동 선택 (예 : 선택)이 생성 될 때만) 더 효율적이라고 생각한다는 것입니다. 컨트롤은 컨트롤에 바인드 된 액션이 실행될 수 있는지 끊임없이 체크합니다 (실행되지 않으면 WPF는 컨트롤을 자동으로 활성화하고 그렇지 않으면 비활성화 된 것처럼 보입니다).

감사합니다.

+0

이 질문에 도움이 될 수 있습니다 : http://stackoverflow.com/questions/845446/wpf-mvvm-commands-are-easy-how-to-connect-view-and-viewmodel-with-routedevent –

답변

1

ListBoxItem에서 파생되고 ICommandSource 인터페이스를 구현하는 사용자 지정 컨트롤을 만들 수도 있습니다. 나는 지금 더 간단한 해결책을 생각할 수 없다.

+0

네 ! 나는 당신의 생각을 좋아합니다. 이것은 당신이 말하는 것처럼 간단한 해결책이 될 것입니다. 이를 수행하는 장점은이 사용자 정의 컨트롤을 다른 클래스에 재사용 할 수 있다는 것입니다. 나는 당신이 말하는 것과 또 다른 훌륭한 해결책 인 로버트의 해결책을 시도 할 것입니다. 좀 도와 주셔서 대단히 감사합니다. – user304602

+0

안녕하세요, 마이크, 귀하의 솔루션을 구현하고 지금 문제가 있습니다. 내 창에 사용자 지정 컨트롤을 배치하고 이제는 종속성 속성으로 클래스에 등록했기 때문에 원하는 명령을 표시 할 수있는 가능성이 있지만 지금은 원하는 Executed 및 CanExecute 메서드를 어떻게 나타낼 수 있습니까? 커스텀 컨트롤의 동작이 커맨드, 실행 및 canExecute를 정의 할 때 버튼과 같았 으면합니다. 감사. – user304602

0

attached behaviors을 살펴보십시오.

+0

우수 기사! 나는 이것을 나의 목록 상자에 적용 해보기 위해 이것을보고있다. 이전에 붙어 있던 행동을 사용하지 않았지만 명령에 대한 또 다른 대안이라고 생각합니다. 그러나 그것을보기 위해 나는 명령이나 붙어있는 행동을 사용하는 것 사이에 약간 혼란 스럽다. 차이점은 무엇입니까? (왜냐하면 그들은 매우 비슷하기 때문입니다) 그리고 언제 다른 것을 사용할 것입니까? 가장 효율적인 것은 무엇입니까? 좋은 답변 주셔서 감사합니다. – user304602

+0

첨부 된 동작이이 경우에 유효하지 않다고 생각합니다.예를 들어, MouseEnter 이벤트 리스너를 처음 등록하면 백그라운드 작업자가 실행 중일 때 mouseEnter 이벤트가 처음 발생할 때 listbox 항목이 비활성화 된 것처럼 보입니다. 그러나 일단 목록 상자 항목이 비활성화되면 배경 작업자가 완료 될 때 활성화 된 상태로 돌아갈 방법이 없습니다. 물론, 비활성화되고 MouseEnter 이벤트가 발생하지 않기 때문입니다. 따라서 첨부 된 동작이 컨트롤을 다시 사용 가능/사용 불가능으로 설정하십시오. 사용을 중지하면 직접 사용 설정하는 방법이 없습니다 – user304602

0

내가 게시 한 첫 번째 질문에 따르면 ListBox에서 CommandBindings를 사용하면 작동하지 않습니다. CanExecute의 구현이었다 : (실행 여부)와 나는 같은 다른 컨트롤을 가지고 있기 때문에 내가 왜 이해하지 못하는 목록 상자 제어 자동으로 비활성화/활성화하지 않습니다 WPF를하고있는 배경 작업자 상태에 따라함으로써

private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e) 
    { 

     e.CanExecute = !cBgWorkers.isRunning;   

    } 

버튼은 명령이 바인드되고 WPF가 명령을 자동으로 활성화/비활성화합니다.

그래서 나는 다음과 같은 수정했을 : 이제

private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e) 
    { 

     e.CanExecute = !cBgWorkers.isRunning; 

     if (LayoutListBox != null) LayoutListBox.IsEnabled = !cBgWorkers.isRunning; 
    } 

을, 그것을 작동합니다.Listbox는 백그라운드 작업자가 실행되지 않을 때 활성화되며, 그렇지 않은 경우에는 비활성화됩니다. 내가 맘에 들지 않는 것은 listBox의 isEnabled 속성을 수동으로 활성화/비활성화 할 수있는 메서드에 마지막 줄이 배치 된 것입니다. 비효율적이어서 CanExecute가 값을 변경할 때만 목록 상자의 isEnabled 속성을 변경하고 싶습니다. 늘어나는만큼, CanExecuteChanged하지만 그것을 구현하는 방법을 모르겠습니다. 어떤 아이디어?

몇 가지 솔루션을 시도한 후에 마이크가 더 쉽고 명확 해졌으며 몇 가지 변경 사항으로 다른 컨트롤에 다시 사용할 수 있기 때문에 마이크 솔루션을 구현하고 있습니다.

1

귀하의 솔루션을 완료했습니다. ListBox에서 파생 된 사용자 정의 사용자 정의 컨트롤을 수행하고 ISourceCommand를 구현 했으므로 이제는 작동합니다. !!!! ;)

내 사용자 정의 클래스 :

<Classes:CommandListBox x:Name="LayoutListBox" 
    Command="{x:Static local:WinMain.rcmd}" 

    <!-- These lines doesn't work I explain it following 
    Executed="CommandBinding_Executed" 
    CanExecute="CommandBinding_CanExecute" 
    --> 

     ... > 

    <...> 

    </Classes:CommandListBox> 

및 창 코드 뒤에 :

using System; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Media; 
using System.Windows.Media.Imaging; 
using System.Windows.Input; 

namespace GParts.Classes 
{ 
public class CommandListBox : ListBox, ICommandSource 
{ 
    public CommandListBox() : base() 
    { 

    } 

    // ICommand Interface Members 
    // Make Command a dependency property so it can use databinding. 
    public static readonly DependencyProperty CommandProperty = 
     DependencyProperty.Register(
      "Command", 
      typeof(ICommand), 
      typeof(CommandListBox), 
      new PropertyMetadata((ICommand)null, 
      new PropertyChangedCallback(CommandChanged))); 

    public ICommand Command 
    { 
     get 
     { 
      return (ICommand)GetValue(CommandProperty); 
     } 
     set 
     { 
      SetValue(CommandProperty, value); 
     } 
    } 

    // Make Command a dependency property so it can use databinding. 
    public static readonly DependencyProperty ExecutedProperty = 
     DependencyProperty.Register(
      "Executed", 
      typeof(object), 
      typeof(CommandListBox), 
      new PropertyMetadata((object)null)); 

    public object Executed 
    { 
     get 
     { 
      return (object)GetValue(ExecutedProperty); 
     } 
     set 
     { 
      SetValue(ExecutedProperty, value); 
     } 
    } 

    // Make Command a dependency property so it can use databinding. 
    public static readonly DependencyProperty CanExecuteProperty = 
     DependencyProperty.Register(
      "CanExecute", 
      typeof(object), 
      typeof(CommandListBox), 
      new PropertyMetadata((object)null)); 

    public object CanExecute 
    { 
     get 
     { 
      return (object)GetValue(CanExecuteProperty); 
     } 
     set 
     { 
      SetValue(CanExecuteProperty, value); 
     } 
    } 

    // Make CommandTarget a dependency property so it can use databinding. 
    public static readonly DependencyProperty CommandTargetProperty = 
     DependencyProperty.Register(
      "CommandTarget", 
      typeof(IInputElement), 
      typeof(CommandListBox), 
      new PropertyMetadata((IInputElement)null)); 

    public IInputElement CommandTarget 
    { 
     get 
     { 
      return (IInputElement)GetValue(CommandTargetProperty); 
     } 
     set 
     { 
      SetValue(CommandTargetProperty, value); 
     } 
    } 

    // Make CommandParameter a dependency property so it can use databinding. 
    public static readonly DependencyProperty CommandParameterProperty = 
     DependencyProperty.Register(
      "CommandParameter", 
      typeof(object), 
      typeof(CommandListBox), 
      new PropertyMetadata((object)null)); 

    public object CommandParameter 
    { 
     get 
     { 
      return (object)GetValue(CommandParameterProperty); 
     } 
     set 
     { 
      SetValue(CommandParameterProperty, value); 
     } 
    } 

    // Command dependency property change callback. 
    private static void CommandChanged(DependencyObject d, 
     DependencyPropertyChangedEventArgs e) 
    { 
     CommandListBox clb = (CommandListBox)d; 
     clb.HookUpCommand((ICommand)e.OldValue,(ICommand)e.NewValue); 
    } 
    // Add a new command to the Command Property. 
    private void HookUpCommand(ICommand oldCommand, ICommand newCommand) 
    { 
     // If oldCommand is not null, then we need to remove the handlers. 
     if (oldCommand != null) 
     { 
      RemoveCommand(oldCommand, newCommand); 
     } 
     AddCommand(oldCommand, newCommand); 
    } 

    // Remove an old command from the Command Property. 
    private void RemoveCommand(ICommand oldCommand, ICommand newCommand) 
    { 
     EventHandler handler = CanExecuteChanged; 
     oldCommand.CanExecuteChanged -= handler; 

     //newCommand.Execute(null); 
     //newCommand.CanExecute(null); 

    } 

    // Add the command. 
    private void AddCommand(ICommand oldCommand, ICommand newCommand) 
    { 
     EventHandler handler = new EventHandler(CanExecuteChanged); 
     canExecuteChangedHandler = handler; 
     if (newCommand != null) 
     { 
      newCommand.CanExecuteChanged += canExecuteChangedHandler; 

      //newCommand.Execute(Executed); 
      //newCommand.CanExecute(CanExecute); 
     } 
    } 
    private void CanExecuteChanged(object sender, EventArgs e) 
    { 

     if (this.Command != null) 
     { 
      RoutedCommand command = this.Command as RoutedCommand; 

      // If a RoutedCommand. 
      if (command != null) 
      { 
       if (command.CanExecute(CommandParameter, CommandTarget)) 
       { 
        this.IsEnabled = true; 
       } 
       else 
       { 
        this.IsEnabled = false; 
       } 
      } 
      // If a not RoutedCommand. 
      else 
      { 
       if (Command.CanExecute(CommandParameter)) 
       { 
        this.IsEnabled = true; 
       } 
       else 
       { 
        this.IsEnabled = false; 
       } 
      } 
     } 
    } 

    // Keep a copy of the handler so it doesn't get garbage collected. 
    private static EventHandler canExecuteChangedHandler; 
} 
} 

내 WinMain.xaml에서

public WinMain() 
    { 
     InitializeComponent(); 

     // Command binding. If I don't do this Executed and CanExecute are not executed 
     CommandBindings.Add(new CommandBinding(rcmd, 
      CommandBinding_Executed, CommandBinding_CanExecute)); 
    } 

    public static RoutedCommand rcmd = new RoutedCommand(); 

    // ExecutedRoutedEventHandler 
    private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e) 
    { 
     // Do stuff 

    } 

    // CanExecuteRoutedEventHandler 
    private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e) 
    { 

     e.CanExecute = !cBgWorkers.isRunning; 

     //if (LayoutListBox != null) LayoutListBox.IsEnabled = !cBgWorkers.isRunning; 
    } 

하지만 같은 문제로이 또 다른 해결책. 마지막 행을 배치하지 않으면 CommandBinding_CanExecute에 주석이 표시됩니다. 백그라운드 작업자가 완료되면 wpf에 의해 목록 상자가 자동으로 활성화되지 않습니다. 이 줄을 넣으면 작동합니다. 무슨 일 이니?

내 코드 조각에서 볼 수 있듯이 명령, 실행 및 canexecute를 지정할 수있는 단추와 동일한 작업을 수행하고 싶습니다. 클래스에 등록하고 listbox에서 메서드를 전달했는지 확인했지만 작동하지 않았습니다. 어떻게해야합니까?

대단히 감사합니다.

0

전체 스레드를 사용할 수 없습니다. 꽤 길다. 어쨌든, ListBoxItem에 명령을 넣으려고한다고 생각했습니다. 내가 본 것으로부터 ListBox를 상속 받았다. ICommandSource의 Executed 및 CanExecute 속성을 지정할 필요가 없습니다. 이것은 사용자 지정 컨트롤이 아닌 RoutedCommand에 지정되어야합니다. 명령을 실행하려면 사용자 정의 컨트롤에 이벤트 핸들러를 제공해야합니다. 예를 들어, 항목이 선택되면 명령을 실행합니다. 여기에 예제가 있습니다.

protected override void OnSelected(RoutedEventArgs e) 
{ 
    base.OnSelected(e); 

    if (this.Command != null) 
    { 
     RoutedCommand command = Command as RoutedCommand; 

     if (command != null) 
     { 
      command.Execute(CommandParameter, CommandTarget); 
     } 
     else 
     { 
      ((ICommand)Command).Execute(CommandParameter); 
     } 
    } 
} 
+0

제 마지막 게시물을 참조하십시오. 여기에 너무 광범위합니다. 감사. – user304602

+0

좋아, 조시 스미스의 RelayCommand에 익숙해 져서 잠시 동안 RoutedCommand를 사용할 수 없었습니다. 원하는 경우 RelayCommand를 사용할 수 있습니다. 내 생각에이 시나리오에 사용하는 것이 더 쉽습니다. 어쨌든 UI 디스패처에서 CommandManager.InvalidateRequerySuggested() 메서드를 호출 했습니까? 다른 디스패처에서 호출 할 수 있습니까? –

+0

내가 너를 이해할 지 모르겠다. 나는 Dispatcher를 참조한다고 생각한다. 컨트롤을 들여다. Dispatcher.Invoke를 사용하여 CommandManager.InvalidateRequerySuggested를 호출하지 않는다. 직접 호출한다. 나는 이것을 시도하지 않았다. . 내가 아는 한 Dispatcher.Invoke를 호출하면 UI 컨트롤을 소유하고 있지 않은 스레드에서 UI 컨트롤을 업데이트 할 수 있습니다. 필자는 항상 cmd Manager를 호출하지 않기 때문에 사용하지 않아도됩니다. 다른 스레드에서 InvalidateRequerySuggested를 메인 스레드에서 호출해야한다고 들었습니다. 그렇지 않으면 가끔씩 작동하지 않을 때가 있습니다. – user304602

관련 문제