2012-07-10 3 views
0

주 창의 버튼을 클릭 할 때 열려있는 사이드 윈도우가 있습니다. 이 사이드 윈도우는 메인 윈도우와는 별도의 스레드에서 생성됩니다.생성자가 객체를 소유하고있는 스레드에 없습니다.

버튼을 처음 클릭하면 모든 것이 잘 작동합니다.. 그런 다음 사이드 윈도우를 닫습니다. 이제 버튼을 클릭하면 InvalidOperationException이 발생합니다.

사이드 윈도우가 닫히면, 사이드 윈도우는 닫히고, 실행되는 쓰레드는 버려지고, 열릴 때마다 전혀 새로운 쓰레드가 생성되어 완전히 새로운 윈도우가 생성됩니다. 예외는 윈도우의 가장 하위 하위 요소의 생성자에서 발생합니다 (즉, UI 객체가 윈도우를 구성하는 데 처음 액세스 된 경우).

다음과 같은 현상이 발생합니다. 버튼의 핸들러는 클릭

public partial class MainWindow : Window 
{ 
    private void OnVariableMonitoringButtonClick(object sender, RoutedEventArgs e) 
    { 
     if (monitoringWindow == null) 
     { 
      Cursor = Cursors.Wait; 
      monitoringWindow = new MonitoringWindowWrapper(); 
      monitoringWindow.Loaded += OnMonitoringWindowLoaded; 
      monitoringWindow.Closed += OnMonitoringWindowClosed; 
      monitoringWindow.Start(); // <--- 
     } 
     else 
     { 
      monitoringWindow.Activate(); 
     } 
     e.Handled = true; 
    } 

    void OnMonitoringWindowLoaded(object sender, EventArgs e) 
    { 
     Dispatcher.Invoke(new Action(() => Cursor = Cursors.Arrow)); 
    } 

    void OnMonitoringWindowClosed(object sender, EventArgs e) 
    { 
     monitoringWindow = null; 
     Dispatcher.Invoke(new Action(() => Cursor = Cursors.Arrow)); 
    } 
} 

MonitoringWindowWrapper 클래스는 단지 Dispatcher 호출 내부 MonitoringWindow 클래스의 관련 방법은 크로스 스레드 호출을 처리하기 위해 랩합니다.

코드는 MonitoringWindowWrapper.Start() 방법 입사 :

partial class MonitoringWindowWrapper 
{ 
    public void Start() 
    { 
     // Construct the window in a parallel thread 
     Thread windowThread = new Thread(LoadMonitoringWindow); 
     windowThread.Name = "Monitoring Window Thread"; 
     windowThread.IsBackground = true; 
     windowThread.SetApartmentState(ApartmentState.STA); 
     windowThread.Start(); // <--- 
    } 

    private void LoadMonitoringWindow() 
    { 
     try 
     { 
      // Construct and set up the window 
      monitoringWindow = new MonitoringWindow(); // <--- 
      monitoringWindow.Loaded += OnMonitoringWindowLoaded; 
      monitoringWindow.Closed += OnMonitoringWindowClosed; 
      monitoringWindow.Show(); 

      // Start window message pump on this thread 
      System.Windows.Threading.Dispatcher.Run(); 
     } 
     catch (Exception e) 
     { 
      // Catch any exceptions in this window to save the application from crashing 
      ErrorMessasgeBox.Show(e.Message); 
      if (Closed != null) Closed(this, new EventArgs()); 
     } 
    } 
} 

코드는 다음 MonitoringWindow.MonitoringWindow() 생성자 진입 및 윈도우 내 최저 부 요소까지 필터 : 아래

public partial class MonitoringWindow : Window 
{ 
    public MonitoringWindow() 
    { 
     InitializeComponent(); // <--- 
     // ... 
    } 
} 

모든 방법 :

public partial class LineGraph : UserControl, IGraph, ISubGraph 
{ 

    public LineGraph() 
    { 
     InitializeComponent(); 
     Brush b = GraphUtilities.BackgroundGradient; 
     Background = b; // EXCEPTION THROWN AT THIS LINE 
     BorderBrush = GraphUtilities.Border; 
    } 
} 

예외 호출 스택은 예외가 발생되는 경우에 나 통찰력 :

System.InvalidOperationException was unhandled by user code 
HResult=-2146233079 
Message=The calling thread cannot access this object because a different thread owns it. 
Source=WindowsBase 
StackTrace: 
    at System.Windows.Threading.Dispatcher.VerifyAccess() 
    at System.Windows.Freezable.ReadPreamble() 
    at System.Windows.Media.GradientStopCollection.OnInheritanceContextChangedCore(EventArgs args) 
    at System.Windows.DependencyObject.OnInheritanceContextChanged(EventArgs args) 
    at System.Windows.DependencyObject.OnInheritanceContextChanged(EventArgs args) 
    at System.Windows.Freezable.AddInheritanceContext(DependencyObject context, DependencyProperty property) 
    at System.Windows.DependencyObject.ProvideSelfAsInheritanceContext(DependencyObject doValue, DependencyProperty dp) 
    at System.Windows.DependencyObject.ProvideSelfAsInheritanceContext(Object value, DependencyProperty dp) 
    at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType) 
    at System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, Object value, PropertyMetadata metadata, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType, Boolean isInternal) 
    at System.Windows.DependencyObject.SetValue(DependencyProperty dp, Object value) 
    at System.Windows.Controls.Control.set_Background(Brush value) 
    at Graphing.LineGraph..ctor() in z:\Documents\Projects\Software\Serial\SerialWindows\Graphing\LineGraph\LineGraph.xaml.cs:line 28 
    at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark) 

내가 생각할 수있는 유일한 이유 것은 다음과 같습니다의 Background 속성에 바인딩 된 다른 스레드에서 만든 개체의 속성이 1
LineGraph 개체? 그러나 내 응용 프로그램에는 그러한 바인딩 (적어도 명시 적으로)이 없으며, 그렇다면 왜 처음에는 작동합니까?
2. Background 속성은 LineGraph 개체에 의해 소유되지 않습니다. 그러나 이것은 말이되지 않습니다.
3. 생성자가 생성중인 개체를 만든 스레드에서 실행되지 않고 있습니다. 다시 말하지만 Visual Studio 디버거에서는 "Monitoring Window Thread"스레드에서 작동하고 있다고합니다.

이 문제를 어떻게 해결할 수 있습니까?

Dispatcher을 사용하여 배경을 설정할 수는 있지만 그 클래스와 다른 모든 UI 요소에 대한 모든 호출에는 지루하고 실제로 문제의 원인을 수정하지 않습니다.

대단히 감사합니다!

답변

2

나는 그 범인이 GraphUtilities.BackgroundGradient 인 것 같지만, GraphUtilities 클래스를 나열하지 않았습니다. 브러쉬는 freezable 개체입니다.

Freezable Objects Overview에서 MSDN에

: 냉동 된 Freezable는 스레드에서 공유 할 수

, 동안 고정 된 Freezable에는 할 수 없습니다.

처음 실행하면 해당 브러시가 모니터링 창 스레드와 연결됩니다. 다음 번에 해당 창을 열면 새로운 스레드입니다. 다른 스레드에서 브러쉬를 사용하려면 브러시에서 Freeze 메서드를 호출해야합니다.

+0

감사합니다. 'Free'()를 호출하는'Brush' 객체를 호출하는'GraphUtilities' 클래스에'static' 생성자를 추가했습니다. 문제가 해결되었습니다! 감사 :) – Brett

1

모든 UI 리소스가 단일 스레드 인 UI 스레드에서 만들어지고 소유된다는 것은 WPF의 전제 조건입니다. 많은 것들은 이것이 그렇게 될 것이라는 가정의 타당성에 달려 있습니다. 이 규정을 준수하지 않으면 모든 배팅이 해제됩니다.는 백그라운드 스레드에 사용을하기 위해 백그라운드 스레드에 대기 표시 UI를 생성에

당신은 필요가 없습니다. 대형 응용 프로그램은 기본 스레드에서 모든 UI를 만듭니다. 장기간 실행되는 활동은 백그라운드 스레드에서 발생합니다. 시간이되면 컨트롤은 UI를 업데이트 할 수있을만큼 길게 UI 스레드에 마샬링합니다.

내가 진행하기 전에 당신에게 read this carefully을 제안한다.


그래서 원하는 답을 얻었습니다. 무엇이 일 수 있다는 사실은이 완료되면 좋은 생각이 아닙니다. 소프트웨어 디자인에서 똑똑한 사람과 똑같은 사람은 거의 없습니다.

+0

나의 이해는 UI 자원이 너무 오래가 STA 모드에서와 같이, 어떤 스레드에서 존재할 수있는 것이 었습니다. 유일한 다른 조건은 해당 스레드를 생성 한 스레드에서만 액세스 할 수 있다는 것입니다. 예를 들어 http://blogs.msdn.com/b/dwayneneed/archive/2007/04/26/multithreaded-ui-hostvisual.aspx를 참조하십시오. 별도의 스레드의 주된 이유는 사이드 윈도우가로드되는 동안 기본 UI를 차단하지 않고 기다리는 동안 사용자에게 바쁜 커서를 표시하는 것입니다. 이것은 대규모 응용 프로그램에서 매우 일반적으로 보일 수 있으므로 가능해야합니다. 감사. – Brett

관련 문제