BitmapCacheを指定したWPFアプリが応答しないことがあったりなかったり
という現象にはまっています
ググると9年前にstack overflowに同様の質問があったのでリンクは貼っておきます
上記質問でも解決方法は示されていません
再現方法
UACの確認ダイアログが出るようにしておきます
WPFアプリを準備します
今回は.NET Framework 4.8で作っています(Coreでも同様です)
以下にプロジェクトを置いておきます
アプリではMainWindow(プロジェクトのテンプレートで作られるやつ)のLoad時にSubWindow(プロジェクトにWindowを一つ追加)を表示してMainWindowは隠します
MainWindow.xaml.cs
public MainWindow() { InitializeComponent(); this.Loaded += MainWindow_Loaded; } private void MainWindow_Loaded(object sender, RoutedEventArgs e) { var subWindow = new SubWindow(); subWindow.Closed += SubWindow_Closed; subWindow.Show(); this.Visibility = Visibility.Collapsed; } private void SubWindow_Closed(object sender, EventArgs e) => this.Close();
SubWindowでは描画処理が動いていることを確認するために1秒周期で現在時刻を更新しています。そしてこのSubWindowのコンテンツ(Grid)にBitmapCacheを指定しておきます
SubWindow.xaml
<Grid> <Grid.CacheMode> <BitmapCache/> </Grid.CacheMode> <TextBlock x:Name="Time"/> </Grid>
SubWindow.xaml.cs
public SubWindow() { InitializeComponent(); this.Loaded += SubWindow_Loaded; } private void SubWindow_Loaded(object sender, RoutedEventArgs e) { ThreadPool.QueueUserWorkItem(_ => { while (true) { Thread.Sleep(1000); Dispatcher.BeginInvoke(new Action(() => Time.Text = DateTime.Now.ToString())); } }); }
アプリを実行するとSubWindowだけが表示されます
UACの確認ダイアログを出す
この状態で管理者権限で立ち上がるもの(管理者モードのPowerShellやタスクマネージャーなど)を起動して確認ダイアログを表示します
再描画しなくなる
SubWindowの描画が止まっていることに気づきます。タスクマネージャーで見るとCPU使用率が異常に高くなっていることがわかります(多分コア一つ占有)
描画はされていませんがイベント等は動くのでアプリの終了なんかはできます
おまけ
上の例ではMainWindowを隠していますがShowInTaskbar = false;
だけでも現象は発生します(ってリンク先のstack overflowにも書いてあったし実際そうなった)
おまけ 2
すべての環境で現象が発生するわけではなさそうです
おまけ 3
現象発生中に画面の解像度やスケールを変更すると復活します
まとめ
今のところMainWindowを隠す時はBitmapCacheは指定しないようにする、という対処療法みたいなことしかできません。 (MaterialDesignColors https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit のスタイルを適用するとBitmapCacheが付いてくるぞ!)
私にはこれ以上の調査は無理です、だれかエロい人おしえて
UWPのターゲットバージョンを上げたらプログレスバー(Storyboard?)の動作が変わっちゃった話
あけましておめでとうございます
令和も早くも2年目に突入した今日この頃、いかがお過ごしでしょうか? 私はいつも通り、過去の遺物をいじる作業でございます
今回の題材はUWP 当時の対象OSは「Windows 10 Fall Creators Update(1709)」です 時の経つのは早いもので、このOSはすでにサポート対象外となっております
NuGet Packageの更新とかもろもろやっていたらターゲットバージョンが古くてビルドが通らなくなったので、ターゲットバージョンを上げたらプログレスバー(Storyboard?)の動作が変わっちゃったよ!というお話
準備
新規のUWPアプリを作ります
MainPage.xaml
<Page.Resources> <Storyboard x:Name="ValueStoryboard"> <DoubleAnimation Storyboard.TargetName="ProgressBar1" Storyboard.TargetProperty="Value" EnableDependentAnimation="True" To="100" Duration="00:00:00.5"/> </Storyboard> </Page.Resources> <Grid Margin="40"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition/> </Grid.RowDefinitions> <ProgressBar x:Name="ProgressBar1" Minimum="0" Maximum="10" Height="20"/> <Slider Grid.Row="1" x:Name="Slider1" Minimum="0" Maximum="20" Value="0" ValueChanged="Slider1_ValueChanged"/> <StackPanel Grid.Row="2" Orientation="Horizontal"> <TextBlock Text="ProgressBar1.Value = "/> <TextBlock Text="{Binding ElementName=ProgressBar1,Path=Value}"/> </StackPanel> <StackPanel Grid.Row="3" Orientation="Horizontal"> <TextBlock Text="Slider1.Value = "/> <TextBlock Text="{Binding ElementName=Slider1,Path=Value}"/> </StackPanel> </Grid>
MainPage.xaml.cs
private void Slider1_ValueChanged(object sender, RangeBaseValueChangedEventArgs e) { var storyBoard = (Storyboard)this.Resources["ValueStoryboard"]; ((DoubleAnimation)storyBoard.Children[0]).To = e.NewValue; storyBoard.Begin(); }
- プログレスバーの最大値は10
- スライダーの最大値は20
- プログレスバーの値はStoryboard(ValueStoryboard)で変更
- スライダーの値が変わるとコードビハインド側でその値をToに設定してStoryboardを実行
というものです
TargetPlatformVersion=10.0.16299.0
まずは1709をターゲットにして試します
こんな感じ。プログレスバーの最大値が10なのでそれ以上の値にはなりません
TargetPlatformVersion=10.0.17134.0
次に1803をターゲットにして試します
はい
プログレスバーの値が最大値を超えます
ちなみに
<ProgressBar x:Name="ProgressBar1" Minimum="0" Maximum="10" Height="20" Value="{Binding ElementName=Slider1,Path=Value}"/>
こんな感じで直接値を入れた場合はちゃんと最大値で止まります
まとめ
1803からこんな感じの動作になったっぽいですね。当時のBreaking changesに書いてあるのかもしれませんが、面倒なので調べていません
それでは、よいお年を
WPFでSystem.Windows.PointがそのままBindingできるのを知らなかった話
.NET Core 3.0で試してます。それ以外は知りません
結論
いつからなのか、ずっとそうなのか、WPFでSystem.Windows.Pointを編集する際にそのままBindingできました
おしまい
以下だらだらと
試してみる
ViewModel
public class MainViewModel { public ReactiveProperty<Point> Point { get; } = new ReactiveProperty<Point>(); public MainViewModel() { Point.Subscribe(p => System.Diagnostics.Debug.WriteLine("Update : " + p)); } }
これだけ、ReactiveProperty
らくちんちん
View (抜粋)
<TextBlock Text="Point"/> <TextBox Grid.Column="1" Text="{Binding Point.Value}"/>
実行結果
できた!
フォーマット指定もできるよ
Validationももろちん
とりあえず思いつく構造体を並べてみた
全部いけた!!
まとめ
見た目を凝ろうと思ったらやっぱりこれだと辛い(数値はNumericUpDownを求められたり)んですが、とりあえず設定ができればいいレベルのものならこれでいいじゃない
いままでいちいち要素毎に分けていたよ・・・