2012-06-12 2 views
2

모달 대화 상자를 시뮬레이트하고 Reactive Extensions가 적절한 도구로 느껴지도록 코드 조각을 업데이트하려하지만 작동하지 못합니다.Reactive Extensions로 UI를 차단하지 않고 IObservable <T>이 완료 될 때까지 기다리는 방법?

현재 코드는 다음과 같이 보입니다 : 지금까지

public bool ShowConfirmationDialogs(IEnumerable<ItemType> items) 
{ 
    bool canContinue = true; 

    foreach (var item in items) 
    { 
     Dialog dialog = new Dialog(); 
     dialog.Prepare(item); // Prepares a "dialog" specific to each item 

     IObservable<bool> o = Service.ShowDialog(dialog, result => 
     { 
      // do stuff with result that may impact next iteration, e.g. 
      canContinue = !result.Condition; 
     }); 

     // tried using the following line to wait for observable to complete 
     // but it blocks the UI thread 
     o.FirstOrDefault(); 

     if (!canContinue) 
      break; 
    } 

    if (!canContinue) 
    { 
     // do something that changes current object's state 
    } 

    return canContinue; 
} 

를, 람다 식의 코드는 ShowDialog으로 표시된 "대화"가 폐쇄되었을 때 물건을 하였다. ShowDialog에 대한 호출이 차단되지 않고 void을 반환하는 데 사용되었습니다.

ShowDialog의 뒤에서 일어나는 일은 ObservableCollection에 추가되어 화면에 표시된다는 것입니다.

ShowDialog을 수정하여 대화 상자가 닫힐 때 OnCompleted 가입자를 호출하는 IObservable<bool>을 반환합니다. 이 작동합니다. 나는 다음과 같은 코드로 테스트 :

o.Subscribe(b => Console.WriteLine(b),() => Console.WriteLine("Completed")); 

하고 대화 상자를 닫을 때 나는 문자열 "Completed"을 볼 수 있습니다. 내 문제는 위의 줄을 비 블로킹, 그래서 내가 할 수있는 몇 가지 대화 상자를 표시 할 수 있습니다.

o.FirstOrDefault(); 

프로그램이 관찰 보내 무언가 또는 완료 될 때까지이 기다릴 것이라고 가정 :

나는 다음 시도했다. 이 프로그램은 모든 권리를 차단하지만, UI도 동결되어 결코 닫을 수없는 대화 상자를 볼 수 없으므로 관측 대상이 완료되지 않습니다.

ObserveOnSubscribeOn을 사용하여 몇 가지 변형을 시도해 보니 UI 스레드는 운이 좋지 않은 상태로 남겨 둡니다. 어떤 아이디어라도 크게 평가 될 것이고, 나의 주요 목표는 순차적으로 보이는 코드를 유지하는 것이다. Window.ShowDialog을 사용할 때와 비슷하다. 위에서 언급 한 바와 같이 예상되는 동작이 Window.ShowDialog를 사용할 때와 동일,을 (그리고 코멘트에 크리스 답변을)

문제는 ShowDialog 비 차단이라고하고 :

을 요약합니다. 지금 막 차단할 수는 있지만 루프가 계속되고 여러 대화 상자가 표시되거나 차단할 수 있습니다 (FirstOrDefault). 또한 UI를 차단하여 관찰 가능 항목을 완료하기 위해 대화 상자를 닫지 못하게합니다.

자세히 설명 : (Enigmativity 용)

I는 제어 모달 인 의미하다 표시 ShowDialog 전화 그 나머지 애플리케이션하지만 호출에 접근하는 것을 차단하는 유저 메서드는 비 블로킹이므로 즉시 실행이 계속됩니다. 필자의 예에서는 루프 때문에 여러 대화 상자를 표시 할 수 있습니다. 이 메서드는 컬렉션에 개체를 추가하고이 동작을 변경할 수 없기 때문에 비 블로킹입니다.

그러나 Rx를 사용하고자하므로 ShowDialogIObservable을 반환합니다.이제 메소드는 즉시 반환되지만, 일단 ShowDialog의 액션에 의해 표시된 컨트롤이 닫히면 어떤 옵저버라도 OnCompleted을 호출 할 객체가 있습니다. 문제가되는 경우이를 위해 Subject을 사용하고 있습니다.

지금 내가 원하는 것은 이동하기 전에 IObservable이 완료되기까지 기다렸다가 차단 호출을 시뮬레이션하는 것입니다. FirstOrDefault은 대기 부분을 성공적으로 처리하지만 유감스럽게도 UI 스레드를 차단하여 컨트롤이 실제로 표시되지 않도록하여 사용자가 UI 스레드를 닫지 못하게하므로 IObservable이 완료되지 않습니다.

x 초 후에 대화 상자를 자동으로 닫음으로써 일종의 일을 할 수 있기 때문에 내 생각을 멀리 할 수 ​​없다는 것을 알고 있습니다. 이제는 사용자가 타이머 대신 컨트롤을 닫을 수 있도록 UI를 차단하지 않는 "대기중인"부분 만 필요합니다.

+0

그래서 전반적인 목표는 무엇입니까? 순차적으로 표시된 대화 상자의 목록을 가지고 있으며 결과 사용자 선택은 아직 표시되지 않은 잠재적 대화 상자의 목록을 변경합니까? –

+0

그건 내 목표 그 자체가 아니지만 부작용입니다. 내가 원하는 것은 단순히 실행을 계속하기 전에 표시된 대화 상자가 닫힐 때까지 기다리는 것입니다. 루프는 필요하지 않지만 대화 상자를 닫은 후 모든 코드를 콜백에 넣을 수없는 이유를 설명하는 데 도움이됩니다. – madd0

+0

대화 상자를 어떻게 보여줄지 모르겠지만 ShowModal 만 사용할 수는 없습니까? http://msdn.microsoft.com/en-us/library/c7ykbedk.aspx –

답변

3

내 문제에 대한 해결책을 찾았으므로 관심있는 경우에 대비하여 알려 드리겠습니다.

일부 리팩터링을 마친 후 필자는이 질문에 사용한 서비스의 메서드 이름을 변경하고 새 서비스를 만들었습니다. 이 인터페이스는 다음과 같습니다 것 : 나는 코멘트가 지금과 같이 사용할 수있는 코드를 이해하기에 충분해야한다고 생각

public bool? ShowDialog(Dialog dialog) 
{ 
    // This will hold the result returned by the dialog 
    bool? result = null; 

    // We show a dialog using the method that returns an IObservable 
    var subject = this.Show(dialog); 

    // but we have to wait for it to close on another thread, otherwise we'll block the UI 
    // we do this by preparing a new DispatcherFrame that exits when we get a value 
    // back from the dialog 
    DispatcherFrame frame = new DispatcherFrame(); 

    // So start observing on a new thread. The Start method will return immediately. 
    new Thread((ThreadStart)(() => 
    { 
     // This line will block on the new thread until the subject sends an OnNext or an OnComplete 
     result = subject.FirstOrDefault(); 

     // once we get the result from the dialog, we can tell the frame to stop 
     frame.Continue = false; 
    })).Start(); 

    // This gets executed immediately after Thread.Start 
    // The Dispatcher will now wait for the frame to stop before continuing 
    // but since we are not blocking the current frame, the UI is still responsive 
    Dispatcher.PushFrame(frame); 

    return result; 
} 

:

public interface IDialogService 
{ 
    /// <summary> 
    /// Displays the specified dialog. 
    /// </summary> 
    /// <remarks>This method is non-blocking. If you need to access the return value of the dialog, you can either 
    /// provide a callback method or subscribe to the <see cref="T:System.IObservable{bool?}" /> that is returned.</remarks> 
    /// <param name="dialog">The dialog to display.</param> 
    /// <param name="callback">The callback to be called when the dialog closes.</param> 
    /// <returns>An <see cref="T:System.IObservable{bool?}" /> that broadcasts the value returned by the dialog 
    /// to any observers.</returns> 
    IObservable<bool?> Show(Dialog dialog, Action<bool?> callback = null); 

    /// <summary> 
    /// Displays the specified dialog. This method waits for the dialog to close before it returns. 
    /// </summary> 
    /// <remarks>This method waits for the dialog to close before it returns. If you need to show a dialog and 
    /// return immediately, use <see cref="M:Show"/>.</remarks> 
    /// <param name="dialog">The dialog to display.</param> 
    /// <returns>The value returned by the dialog.</returns> 
    bool? ShowDialog(Dialog dialog); 
} 

내 문제를 해결 부분은 ShowDialog의 구현 그래서 :

public bool? ShowConfirmationDialogs(IEnumerable<ItemType> items) 
{ 
    bool canContinue = true; 

    foreach (var item in items) 
    { 
     Dialog dialog = new Dialog(); 
     dialog.Prepare(item); // Prepares a "dialog" specific to each item 

     bool? result = Service.ShowDialog(dialog); 

     canContinue = result.HasValue && result.Value; 

     if (!canContinue) 
      break; 
    } 

    if (!canContinue) 
    { 
     // do something that changes current object's state 
    } 

    return canContinue; 
} 

나는 어떤 의견이나 다른 사용자가 가질 수있는 대안을 듣고 싶네요.

1

코드는 관찰 가능하고 열거 가능한 시퀀스의 홀수 혼합입니다. 또한 observables를 사용하는 함수에서 bool을 반환하려고하므로 차단 작업이 필요하다는 잘못된 습관을 강요합니다.

모든 것을 관찰 가능 상태로 유지하는 것이 가장 좋습니다. 이것이 최상의 방법입니다.

또한 item의 기능은 ShowDialog에 명확하게 사용되지 않습니다.

지금은 내가 모르는 사이에 제공 할 수있는 최고입니다. 이것을 시도하십시오 :

public IObservable<bool> ShowConfirmationDialogs(IEnumerable items) 
{ 
    var query = 
     from item in items.OfType<SOMEBASETYPE>().ToObservable() 
     from result in Service.ShowDialog(item => 
     { 
      // do stuff with result that may impact next iteration of foreach 
     }) 
     select new 
     { 
      Item = item, 
      Result = result, 
     }; 

    return query.TakeWhile(x => x.Result == true); 
} 

호출 코드는 UI 스레드에서 관찰해야합니다.

도움이 될지 알려주세요.

+0

반환 된'bool'을 잊어 버리고 루프를 잊어 버려라. 내가 원한 것은 ShowDialog에서 IObservable을 다시 얻고 (UI를 돌 보았습니다) UI가 멈추지 않고 계속 진행되기 전에 완료 될 때까지 기다리는 것뿐입니다. 이것이 가능한가? – madd0

+0

@ madd0 - 그래서'ShowDialog'는 모달이 아닙니다. 따라서 대화가 표시되는 동안 다른 양식을 사용할 수 있습니까? 달성하려는 워크 플로를 좀 더 자세히 설명 할 수 있습니까? – Enigmativity

+0

"양식", "양식"등은 구현 세부 사항입니다. 희망 사항을 명확하게하기 위해 질문 본문에 추가 설명을 추가했습니다. – madd0

관련 문제