[c#] WPF에서 Application.DoEvents ()는 어디에 있습니까?

버튼을 누를 때마다 확대 / 축소되는 다음 샘플 코드가 있습니다.

XAML :

<Window x:Class="WpfApplication12.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">

    <Canvas x:Name="myCanvas">

        <Canvas.LayoutTransform>
            <ScaleTransform x:Name="myScaleTransform" />
        </Canvas.LayoutTransform>

        <Button Content="Button" 
                Name="myButton" 
                Canvas.Left="50" 
                Canvas.Top="50" 
                Click="myButton_Click" />
    </Canvas>
</Window>

* .cs

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void myButton_Click(object sender, RoutedEventArgs e)
    {
        Console.WriteLine("scale {0}, location: {1}",
            myScaleTransform.ScaleX,
            myCanvas.PointToScreen(GetMyByttonLocation()));

        myScaleTransform.ScaleX =
            myScaleTransform.ScaleY =
            myScaleTransform.ScaleX + 1;

        Console.WriteLine("scale {0}, location: {1}",
            myScaleTransform.ScaleX,
            myCanvas.PointToScreen(GetMyByttonLocation()));
    }

    private Point GetMyByttonLocation()
    {
        return new Point(
            Canvas.GetLeft(myButton),
            Canvas.GetTop(myButton));
    }
}

출력은 다음과 같습니다.

scale 1, location: 296;315
scale 2, location: 296;315

scale 2, location: 346;365
scale 3, location: 346;365

scale 3, location: 396;415
scale 4, location: 396;415

보시다시피, 사용하여 해결한다고 생각한 문제가 Application.DoEvents();있지만 … NET 4 에는 선험적 이지 않습니다 .

무엇을해야합니까?



답변

이전 Application.DoEvents () 메서드는 설명 된대로 처리를 수행하기 위해 Dispatcher 또는 백그라운드 작업자 스레드 를 사용하기 위해 WPF에서 더 이상 사용되지 않습니다 . 두 개체를 사용하는 방법에 대한 몇 가지 문서에 대한 링크를 참조하십시오.

Application.DoEvents ()를 반드시 사용해야하는 경우 단순히 system.windows.forms.dll을 응용 프로그램으로 가져 와서 메서드를 호출 할 수 있습니다. 그러나 이것은 WPF가 제공하는 모든 이점을 잃어 버리기 때문에 권장되지 않습니다.


답변

이런 식으로 시도

public static void DoEvents()
{
    Application.Current.Dispatcher.Invoke(DispatcherPriority.Background,
                                          new Action(delegate { }));
}


답변

글쎄, 방금 Dispatcher 스레드에서 실행되는 메서드에서 작업을 시작하고 UI 스레드를 차단하지 않고 차단해야하는 경우가 발생했습니다. msdn은 Dispatcher 자체를 기반으로 DoEvents ()를 구현하는 방법을 설명합니다.

public void DoEvents()
{
    DispatcherFrame frame = new DispatcherFrame();
    Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
        new DispatcherOperationCallback(ExitFrame), frame);
    Dispatcher.PushFrame(frame);
}

public object ExitFrame(object f)
{
    ((DispatcherFrame)f).Continue = false;

    return null;
}

( Dispatcher.PushFrame 메서드 에서 가져옴 )

일부는 동일한 논리를 적용하는 단일 방법을 선호 할 수 있습니다.

public static void DoEvents()
{
    var frame = new DispatcherFrame();
    Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
        new DispatcherOperationCallback(
            delegate (object f)
            {
                ((DispatcherFrame)f).Continue = false;
                return null;
            }),frame);
    Dispatcher.PushFrame(frame);
}


답변

업데이트 창 그래픽이 필요한 경우 다음과 같이 사용하는 것이 좋습니다.

public static void DoEvents()
{
    Application.Current.Dispatcher.Invoke(DispatcherPriority.Render,
                                          new Action(delegate { }));
}


답변

myCanvas.UpdateLayout();

잘 작동하는 것 같습니다.


답변

두 가지 제안 된 접근 방식의 한 가지 문제점은 유휴 CPU 사용량 (내 경험상 최대 12 %)이 수반된다는 것입니다. 예를 들어 모달 UI 동작이이 기술을 사용하여 구현되는 경우와 같이 일부 경우에 이는 차선책입니다.

다음 변형은 타이머를 사용하여 프레임 사이에 최소 지연을 도입합니다 (여기서는 Rx로 작성되지만 일반 타이머로 달성 할 수 있음).

 var minFrameDelay = Observable.Interval(TimeSpan.FromMilliseconds(50)).Take(1).Replay();
 minFrameDelay.Connect();
 // synchronously add a low-priority no-op to the Dispatcher's queue
 Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() => minFrameDelay.Wait()));


답변

도입 이후 asyncawaitA (이전)를 통해 UI 스레드 도중에 포기 위해 가능해 코드 동기 블록 * 사용 Task.Delay

private async void myButton_Click(object sender, RoutedEventArgs e)
{
    Console.WriteLine("scale {0}, location: {1}",
        myScaleTransform.ScaleX,
        myCanvas.PointToScreen(GetMyByttonLocation()));

    myScaleTransform.ScaleX =
        myScaleTransform.ScaleY =
        myScaleTransform.ScaleX + 1;

    await Task.Delay(1); // In my experiments, 0 doesn't work. Also, I have noticed
                         // that I need to add as much as 100ms to allow the visual tree
                         // to complete its arrange cycle and for properties to get their
                         // final values (as opposed to NaN for widths etc.)

    Console.WriteLine("scale {0}, location: {1}",
        myScaleTransform.ScaleX,
        myCanvas.PointToScreen(GetMyByttonLocation()));
}

솔직히 말해서 위의 정확한 코드로 시도하지는 않았지만 ItemsControl값 비싼 항목 템플릿이있는에 많은 항목을 배치 할 때 타이트한 루프에서 사용하고 때로는 다른 항목에 약간의 지연을 추가합니다. UI에 더 많은 시간이 있습니다.

예를 들면 :

        var levelOptions = new ObservableCollection<GameLevelChoiceItem>();

        this.ViewModel[LevelOptionsViewModelKey] = levelOptions;

        var syllabus = await this.LevelRepository.GetSyllabusAsync();
        foreach (var level in syllabus.Levels)
        {
            foreach (var subLevel in level.SubLevels)
            {
                var abilities = new List<GamePlayingAbility>(100);

                foreach (var g in subLevel.Games)
                {
                    var gwa = await this.MetricsRepository.GetGamePlayingAbilityAsync(g.Value);
                    abilities.Add(gwa);
                }

                double PlayingScore = AssessmentMetricsProcessor.ComputePlayingLevelAbility(abilities);

                levelOptions.Add(new GameLevelChoiceItem()
                    {
                        LevelAbilityMetric = PlayingScore,
                        AbilityCaption = PlayingScore.ToString(),
                        LevelCaption = subLevel.Name,
                        LevelDescriptor = level.Ordinal + "." + subLevel.Ordinal,
                        LevelLevels = subLevel.Games.Select(g => g.Value),
                    });

                await Task.Delay(100);
            }
        }

Windows Store에서 컬렉션에 멋진 테마 전환이있을 때 그 효과는 매우 바람직합니다.

루크

  • 주석을 참조하십시오. 내 대답을 빠르게 작성할 때 동기 코드 블록을 가져온 다음 스레드를 호출자에게 다시 넘기는 행위에 대해 생각하고 있었는데, 그 결과 코드 블록이 비 동기화되었습니다. 독자들이 Servy와 내가 무슨 말을하는지 알 수 없기 때문에 내 대답을 완전히 바꾸고 싶지 않습니다.