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()
{
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()));
Since the introduction of async
and await
its now possible to relinquish the UI thread partway through a (formerly)* synchronous block of code using Task.Delay
, e.g.
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()));
}
I'll be honest, I've not tried it with the exact code above, but I use it in tight loops when I'm placing many items into an ItemsControl
which has an expensive item template, sometimes adding a small delay to give the other stuff on the UI more time.
For example:
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);
}
}
On Windows Store, when there's a nice theme transition on the collection, the effect is quite desirable.
Luke
- see comments. When I was quickly writing my answer, I was thinking about the act of taking a synchronous block of code and then relinquishing the thread back to its caller, the effect of which makes the block of code asynchronous. I don't want to completely rephrase my answer because then readers can't see what Servy and I were bickering about.
참고URL : https://stackoverflow.com/questions/4502037/where-is-the-application-doevents-in-wpf
'Program Tip' 카테고리의 다른 글
앱 확장과 함께 Cocoapods 사용 (0) | 2020.10.06 |
---|---|
SQL Server의 테이블 및 인덱스 크기 (0) | 2020.10.06 |
awk substr을 사용하여 마지막 필드 가져 오기 (0) | 2020.10.06 |
JSON의 유효성을 검사하는 정규식 (0) | 2020.10.06 |
devise의 sign_in 작업에 대한 다른 레이아웃 (0) | 2020.10.06 |