WPF에서 커서를 변경하는 것은 때때로 작동하지만 때로는 작동하지 않습니다.


123

여러 사용자 컨트롤에서 다음을 사용하여 커서를 변경합니다.

this.Cursor = Cursors.Wait;

내가 뭔가를 클릭 할 때.

이제 버튼 클릭으로 WPF 페이지에서 동일한 작업을 수행하고 싶습니다. 버튼 위로 마우스를 가져 가면 커서가 손 모양으로 바뀌지 만 클릭하면 대기 커서로 바뀌지 않습니다. 이것이 버튼이라는 사실과 관련이 있는지, 아니면 이것이 사용자 컨트롤이 아닌 페이지이기 때문인지 궁금합니다. 이것은 이상한 행동처럼 보입니다.

답변:


211

특정 페이지 / 사용자 제어 위에있을 때만 커서가 "대기"커서가되어야합니까? 그렇지 않은 경우 Mouse.OverrideCursor 사용하는 것이 좋습니다 .

Mouse.OverrideCursor = Cursors.Wait;
try
{
    // do stuff
}
finally
{
    Mouse.OverrideCursor = null;
}

이것은 UI의 일부가 아닌 애플리케이션의 커서를 재정의하므로 설명하는 문제가 사라집니다.


대답 과 비슷합니다. 3 년 후 (거의 정확히!). 이 질문에 대답하지만, 간단한 같은 나는 :) 항상 가장 유혹
로빈 Maben

이 솔루션은 커서를 "대기"커서로 변경하지만 추가 마우스 입력을 비활성화하지는 않습니다. 이 솔루션을 사용해 보았지만 마우스가 대기 커서로 변경되었지만 여전히 내 WPF 응용 프로그램 내에서 아무 문제없이 UI 요소를 클릭 할 수 있습니다. 대기 커서가 활성화되어있는 동안 사용자가 실제로 마우스를 사용하지 못하도록 방지 할 수있는 방법이 있습니까?
Thomas Huber

2
오래되고있는 그대로 받아 들여지지 만, 올바른 대답이 아닙니다. 앱 커서를 재정의하는 것은 제어 커서를 재정의하는 것과 다릅니다 (두 번째는 WPF에 문제가 있음). 앱 커서를 재정의하면 불쾌한 부작용이 발생할 수 있습니다. 예를 들어 팝업 (오류) 메시지 상자가 동일한 재정의 된 커서를 잘못 사용하도록 강제 할 수 있지만 의도는 마우스가 실제 및 활성 컨트롤 위에있는 동안 재정의하는 것뿐입니다.
Gábor

64

응용 프로그램에서이 작업을 수행하는 한 가지 방법은 IDisposable을 사용한 다음 using(){}블록을 사용하여 완료시 커서가 재설정되도록하는 것입니다.

public class OverrideCursor : IDisposable
{

  public OverrideCursor(Cursor changeToCursor)
  {
    Mouse.OverrideCursor = changeToCursor;
  }

  #region IDisposable Members

  public void Dispose()
  {
    Mouse.OverrideCursor = null;
  }

  #endregion
}

그런 다음 코드에서 :

using (OverrideCursor cursor = new OverrideCursor(Cursors.Wait))
{
  // Do work...
}

대체는 다음 중 하나 일 때 종료됩니다. using 문의 끝에 도달하거나; 예외가 발생하고 제어가 명령문이 끝나기 전에 명령문 블록을 떠나는 경우.

최신 정보

커서 깜박임을 방지하려면 다음을 수행하십시오.

public class OverrideCursor : IDisposable
{
  static Stack<Cursor> s_Stack = new Stack<Cursor>();

  public OverrideCursor(Cursor changeToCursor)
  {
    s_Stack.Push(changeToCursor);

    if (Mouse.OverrideCursor != changeToCursor)
      Mouse.OverrideCursor = changeToCursor;
  }

  public void Dispose()
  {
    s_Stack.Pop();

    Cursor cursor = s_Stack.Count > 0 ? s_Stack.Peek() : null;

    if (cursor != Mouse.OverrideCursor)
      Mouse.OverrideCursor = cursor;
  }

}

2
사용 부분에 좋은 솔루션입니다. 실제로 일부 프로젝트에서 정확히 똑같이 썼습니다 (스택없이). 사용법을 단순화 할 수있는 한 가지는 다음과 같이 작성하는 것입니다. 사용하지 않을 변수를 할당하는 대신 (new OverrideCursor (Cursors.Wait)) {// do stuff} 사용.
Olli

1
필요하지 않습니다. 사용자가 설정 한 경우 Mouse.OverrideCursornull이 설정되지 더 이상 오버라이드 시스템 커서. 경우 내가 직접 현재 커서를 수정했다 (즉 무시하지) 다음에 문제가있을 수 있습니다.
Dennis

2
이것은 좋지만 여러 뷰가 동시에 커서를 업데이트하는 경우 안전하지 않습니다. ViewA 세트의 커서를 놓고 ViewB가 다른 커서를 설정 한 다음 ViewA가 커서를 재설정하려고 시도하는 경쟁 조건에 쉽게 진입 할 수 있습니다 (그런 다음 ViewB를 스택에서 꺼내고 ViewA의 커서를 활성 상태로 둡니다). ViewB가 커서를 재설정하기 전까지는 정상으로 돌아갑니다.
Simon Gillbee

2
@SimonGillbee 정말 가능합니다-제가 이것을 썼을 때 10 년 전의 문제가 아니 었습니다. 아마도를 사용하여 해결책을 찾으면 ConcurrentStack<Cursor>위의 답변을 편집하거나 직접 추가하십시오.
Dennis

2
@Dennis 나는 실제로 며칠 전에 이것을 썼습니다 (그래서 내가 SO를 통해보고 있었던 이유입니다). ConcurrentStack으로 플레이했지만 잘못된 컬렉션으로 판명되었습니다. 스택을 사용하면 상단에서 튀어 나올 수 있습니다. 이 경우 스택의 맨 위가 배치되기 전에 커서가 배치 된 경우 스택의 중간에서 제거하려고합니다. 동시 액세스를 제어하기 위해 ReaderWriterLockSlim과 함께 List <T>를 사용했습니다.
Simon Gillbee

38

버튼에서 데이터 트리거 (뷰 모델 포함)를 사용하여 대기 커서를 활성화 할 수 있습니다.

<Button x:Name="NextButton"
        Content="Go"
        Command="{Binding GoCommand }">
    <Button.Style>
         <Style TargetType="{x:Type Button}">
             <Setter Property="Cursor" Value="Arrow"/>
             <Style.Triggers>
                 <DataTrigger Binding="{Binding Path=IsWorking}" Value="True">
                     <Setter Property="Cursor" Value="Wait"/>
                 </DataTrigger>
             </Style.Triggers>
         </Style>
    </Button.Style>
</Button>

다음은 뷰 모델의 코드입니다.

public class MainViewModel : ViewModelBase
{
   // most code removed for this example

   public MainViewModel()
   {
      GoCommand = new DelegateCommand<object>(OnGoCommand, CanGoCommand);
   }

   // flag used by data binding trigger
   private bool _isWorking = false;
   public bool IsWorking
   {
      get { return _isWorking; }
      set
      {
         _isWorking = value;
         OnPropertyChanged("IsWorking");
      }
   }

   // button click event gets processed here
   public ICommand GoCommand { get; private set; }
   private void OnGoCommand(object obj)
   {
      if ( _selectedCustomer != null )
      {
         // wait cursor ON
         IsWorking = true;
         _ds = OrdersManager.LoadToDataSet(_selectedCustomer.ID);
         OnPropertyChanged("GridData");

         // wait cursor off
         IsWorking = false;
      }
   }
}

4
나도 반대표를 얻지 못합니다. 이 대답은 MVvM (코드 숨김이 없음)을 사용하고 특정 컨트롤에 대한 커서를 제어하려는 경우에 유용합니다. 굉장히 유용하다.
Simon Gillbee

4
저는 MVVM의 이점을 활용하고 있으며 이것이 완벽한 답입니다.
g1ga

MVVM, viewmodels 등에서 더 잘 작동 할 것이라고 믿기 때문에이 솔루션이 마음에 듭니다.
Rod

이 코드에서 볼 수있는 문제는 마우스가 버튼 위에있는 동안에 만 커서가 "대기"라는 것입니다.하지만 마우스를 밖으로 움직이면 "화살표"로 돌아갑니다.
스파이더 맨

7

응용 프로그램이 비동기 작업을 사용하고 마우스 커서를 조작하는 경우 기본 UI 스레드에서만 수행하고 싶을 것입니다. 이를 위해 앱의 Dispatcher 스레드를 사용할 수 있습니다.

Application.Current.Dispatcher.Invoke(() =>
{
    // The check is required to prevent cursor flickering
    if (Mouse.OverrideCursor != cursor)
        Mouse.OverrideCursor = cursor;
});

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.