[WPF] TextBoxのキャレットが消える問題
TextBoxをScaleTransformで縮小するとキャレット(カーソルの場所の縦棒|)がたまに消えます。
まあ、キャレットの1ドットも一緒に縮小されて消えちゃうんだろうなーと思いますが。。。
解決方法はScaleTransformを使わないことです。
もしも縮小していたらInverseとかで100%に戻せばバグりません。
拡大縮小についてはFontSizeでなんとかしろということでした。
GitHub Microsoft
GitHubにMicrosoft公式のリポジトリがあります。
Microsoft · GitHub
いろんなサンプルがあるので参考になりそうです。
GitHub - Microsoft/Windows-classic-samples: This repo contains samples that demonstrate the API used in Windows classic desktop applications.
GitHub - Microsoft/WPF-Samples: Repository for WPF related samples
GitHub - Microsoft/Windows-universal-samples: API samples for the Universal Windows Platform.
StaticPropertyの変更通知
かねがねStaticなプロパティの変更通知ができないか困っておりました。
ありました。
StaticPropertyChanged
.Net 4.5からだそうで、英語読めないので見つけられませんでした。
よくわからないのですが、StaticPropertyChangedイベントを呼び出せばいいみたいです。
staticなのでinterfaceとか使えないので勝手にイベントを追加するだけのようです。
public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged;
こういうことだー
using System; using System.ComponentModel; using System.Runtime.CompilerServices; namespace ねーむすぺーす.Models { public class TestClass { static public string CurrentValue { get { return _CurrentValue; } set { if( _CurrentValue != value ) { _CurrentValue = value; NotifyStaticPropertyChanged(); } } } static private string _CurrentValue = "てすと"; public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged; private static void NotifyStaticPropertyChanged( [CallerMemberName] string propertyName = "" ) { if( StaticPropertyChanged != null ) StaticPropertyChanged( null, new PropertyChangedEventArgs( propertyName ) ); } } }
こう使います。
<!-- m に対してネームスペースを追加して --> <Window xmlns:m="clr-namespace:ねーむすぺーす.Models"> <!-- Path は省略できません Staticな部分の()カッコ重要 --> <TextBlock Text="{Binding Path=(m:TestClass.CurrentValue)}" />
Staticのクラス内を参照する場合はこんな感じになります。
※プロパティ名とか適当です
<TextBlock Text="{Binding Path=(m:TestClass.CurrentModel).Value}"/>
追記:
()カッコで簡潔に記述できるのですが、Xamlデザイナーがエラー出まくるのでXamlの方でリソースを作成したほうが良さそうです。
<Window.Resources> <m:TestClassx:Key="TestClass" /> </Window.Resources> <TextBlock Text="{Binding CurrentValue, Source={StaticResource TestClass}}" /> <!-- クラス(TestClass)がstatic(public static class TestClass {...})ならリソースの作成は必要ないです。--> <TextBlock Text="{Binding CurrentValue, Source={x:Static m:TestClass}}" />
詳しくは参考URLへどうぞ(丸投げ)
参考:
WPF 4.5 での バインディング強化
WPF 4.5: Binding and change notification for static properties - Pete Brown's 10rem.net
WPF 4.5 – Part 9 : binding to static properties UPDATED | Jonathan ANTOINE's thoughts on Windows development
[WPF] ItemsSource に List<String> を指定しても編集できませんので
List<String>の中身を軽く編集しようと思って、DataGridやListBoxのItemsSourceに指定したら動かないっ・・・
こんなListを
public List<String> Items { get { return _Items; } set { _Items = value; } } private List<string> _Items = new List<String> { "One", "Two", "Three" };
簡単編集だ!
<!-- 編集するとエラー (両方向のバインドには、Path または XPath が必要です。) --> <DataGrid ItemsSource="{Binding Items}"> <DataGrid.Columns> <DataGridTextColumn Binding="{Binding .}" /> </DataGrid.Columns> </DataGrid> <!-- 編集しても反映されない --> <ListBox ItemsSource="{Binding Items}"> <ListBox.ItemTemplate> <DataTemplate> <TextBox Text="{Binding .}" /> </DataTemplate> </ListBox.ItemTemplate> </ListBox>
動かない!
Path にドット.つけてるのに😭
はい、ドット.とか無意味なんですね。
バインドしている対象が値型(文字、数値など)では参照を変更できないので編集はできません!
諦めましょう。
何かしらのクラスに内包してやるしか無いみたいです。
こんな感じにして
public static List<String> Items = new List<String> { "One", "Two", "Three" }; public List<BindingProxy<string>> ItemsProxy { set { _ItemsProxy = value; } get { return _ItemsProxy; } } private List<BindingProxy<string>> _ItemsProxy = new List<BindingProxy<string>>( Items.Select( x => new BindingProxy<string>( x ) ) ); public class BindingProxy<T> { public BindingProxy() {} public BindingProxy( T value ) { this.Value = value; } public T Value { get; set; } }
今度こそ簡単編集だ!
<DataGrid ItemsSource="{Binding ItemsProxy}"> <DataGrid.Columns> <DataGridTextColumn Binding="{Binding Value}" /> </DataGrid.Columns> </DataGrid> <ListBox ItemsSource="{Binding ItemsProxy}"> <ListBox.ItemTemplate> <DataTemplate> <TextBox Text="{Binding Value}" /> </DataTemplate> </ListBox.ItemTemplate> </ListBox>
あとでItemsProxyをItemsに戻してデータ編集完了です。
[WPF] Bindign用のConverterの書き方
普通の書き方
だいたいConverterの使い方として説明されるのがこんな感じだと思います。
namespace Namespace.Converters { public class CustomConverter: IValueConverter { public object Convert( object value, Type targetType, object parameter, CultureInfo culture ) { // ここに処理を書く throw new NotImplementedException(); } public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture ) { // ここに処理を書く throw new NotImplementedException(); } } }
Namespaceとかは適当 <Window xmlns:convert="clr-namespace:Namespace.Converters"> <Window.Resources> <convert:CustomConverter x:Key="CustomConverter"/> </Window.Resources> <TextBlock Text="{Binding value, Converter={StaticResource CustomConverter}}"/> </Window>
はい、Resourcesにいちいち入れておくのめんどくさいですね。
直接書く
直接書いてやりましょう。
<Window xmlns:convert="clr-namespace:Namespace.Converters"> <TextBlock> <TextBlock.Text> <Binding Path="value"> <Binding.Converter> <convert:CustomConverter/> </Binding.Converter> </Binding> </TextBlock.Text> </TextBlock> </Window>
XAML長すぎでしょ・・・
Staticインスタンスを作っておく
XAML長すぎ問題がなんとかならないかと探していたところ、”Static作っとけばいいじゃん”みたいな書き込みを見つけました。
public class CustomConverter: IValueConverter { // Staticのインスタンスを作っちゃう private static CustomConverter _instance = new CustomConverter(); public static CustomConverter Instance { get { return _instance; } } public object Convert( object value, Type targetType, object parameter, CultureInfo culture ) { // ここに処理を書く throw new NotImplementedException(); } public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture ) { // ここに処理を書く throw new NotImplementedException(); } }
呼び出すときはこんな感じ
<Window xmlns:convert="clr-namespace:Namespace.Converters"> <TextBlock Text="{Binding value, Converter={x:Static convert:CustomConverter.Instance}}"/> </Window>
便利になりました。
ただ、Template内で使用するとデザイナーがエラー表示をだします。(VS2013)
(リソースを解決できません みたいなエラー)
コンパイルもできるし、実行も問題ないんですけどね・・・
MarkupExtensionを使う
さらに、MarkupExtension を継承しておけばStaticすらいらなくなります。
public class CustomConverter: MarkupExtension, IValueConverter { public object Convert( object value, Type targetType, object parameter, CultureInfo culture ) { // ここに処理を書く throw new NotImplementedException(); } public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture ) { // ここに処理を書く throw new NotImplementedException(); } public override object ProvideValue( IServiceProvider serviceProvider ) { return this; } }
<Window xmlns:convert="clr-namespace:Namespace.Converters"> <TextBlock Text="{Binding value, Converter={convert:CustomConverter}}"/> </Window>
😃😃😃😃😃😃😃😃😃
こういうテクニックはなかなか見つからないですね。
参考:
WPF:Converterを使うとき、xamlのResource内で宣言をしたくないときの書き方 - RunningCSharp
WPF: How to use converters without creating static resources
Visual Studio 不具合一覧
ブレークポイントが別のプロジェクトでも有効となる
- debugging - Visual Studio breakpoints break in the wrong source file (or multiple files simultaneously) if multiple files have the same name - Stack Overflow
- https://connect.microsoft.com/VisualStudio/feedback/details/774195/
Visual Stuido 2015 - Update3 → 未解決
解決方法:同じファイル名にしないようにしましょう・・・😭😭😭
クリック範囲、タッチ範囲、ダブルクリックの猶予時間について 補足
クリック範囲、タッチ範囲、ダブルクリックの猶予時間について - kitunechan’s blogの補足です
WPFでの話です。Windows Formsでは意味が無いかもしれない
MouseDoubleClickイベントを使うとマウスの範囲で判定されてしまいます。
指でのダブルタッチをうまく認識させるためにはMouseDownイベントでClickCountが2の時で判定するとうまいくいきます。
ダブルタッチを判定したくても、TouchDoubleClickイベントは存在しないので気をつけてください!!!
クリック範囲、タッチ範囲、ダブルクリックの猶予時間について
ダブルクリック範囲
System.Windows.Forms.SystemInformation.DoubleClickSize
https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/SystemInformation.cs
タッチ範囲は System.Windows.Input.StylusLogic をリフレクションで取得すること
ただし.Net4.6以降じゃないと記述が足りないので.Net4.6のソースからコピーしましょう
https://referencesource.microsoft.com/#PresentationCore/Core/CSharp/System/Windows/Input/Stylus/StylusLogic.cs
[追記]
StylusLogic.csは.Net4.7のReferenceSourceで公開されていません。
// 謎
DoubleTapDelta
DoubleTapDeltaTime
// タッチ
touchDoubleTapDelta
touchDoubleTapDeltaTime
// ペン
stylusDoubleTapDelta
stylusDoubleTapDeltaTime
/// <summary> /// .net 4.5 だと ラッパー では足りないため、コピーした /// TouchModeN_DtapDist、TouchModeN_DtapTimeがない /// </summary> public class StylusLogic { public static StylusLogic Current = new StylusLogic(); public StylusLogic() { ReadSystemConfig(); } #region DoubleTapDelta /// <summary> /// ダブルタップの有効範囲を取得します /// </summary> public int DoubleTapDelta { get { //if( _DoubleTapDelta == -1 ) { // _DoubleTapDelta = (int)stylusLogicType.InvokeMember( "DoubleTapDelta", BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.NonPublic, null, stylusLogic, null ); //} return _DoubleTapDelta; } } int _DoubleTapDelta = -1; #endregion #region DoubleTapDeltaTime /// <summary> /// ダブルタップの有効速度を取得します /// </summary> public int DoubleTapDeltaTime { get { return _DoubleTapDeltaTime; } } int _DoubleTapDeltaTime = -1; #endregion #region touchDoubleTapDelta /// <summary> /// タッチでのダブルタップの有効範囲を取得します /// </summary> public int touchDoubleTapDelta { get { return _touchDoubleTapDelta; } } int _touchDoubleTapDelta = 45; #endregion #region touchDoubleTapDeltaTime /// <summary> /// タッチでのダブルタップの有効速度を取得します /// </summary> public int touchDoubleTapDeltaTime { get { return _touchDoubleTapDeltaTime; } } int _touchDoubleTapDeltaTime = 300; #endregion #region stylusDoubleTapDelta /// <summary> /// スタイラスでのダブルタップの有効範囲を取得します /// </summary> public int stylusDoubleTapDelta { get { return _stylusDoubleTapDelta; } } int _stylusDoubleTapDelta = 15; #endregion #region stylusDoubleTapDeltaTime /// <summary> /// スタイラスでのダブルタップの有効速度を取得します /// </summary> public int stylusDoubleTapDeltaTime { get { return _stylusDoubleTapDeltaTime; } } int _stylusDoubleTapDeltaTime = 800; #endregion private const double DoubleTapMinFactor = 0.7; // 70% of the default threshold. private const double DoubleTapMaxFactor = 1.3; // 130% of the default threshold. private int _cancelDelta = 10; /// <summary> /// Grab the defualts from the registry for the double tap thresholds. /// </summary> ///<SecurityNote> /// Critical - Asserts read registry permission... /// - TreatAsSafe boundry is the constructor /// - called by constructor ///</SecurityNote> [SecurityCritical] private void ReadSystemConfig() { object obj; RegistryKey stylusKey = null; // This object has finalizer to close the key. RegistryKey touchKey = null; // This object has finalizer to close the key. // Acquire permissions to read the one key we care about from the registry new RegistryPermission( RegistryPermissionAccess.Read, "HKEY_CURRENT_USER\\Software\\Microsoft\\Wisp" ).Assert(); // BlessedAssert try { stylusKey = Registry.CurrentUser.OpenSubKey( "Software\\Microsoft\\Wisp\\Pen\\SysEventParameters" ); if( stylusKey != null ) { obj = stylusKey.GetValue( "DblDist" ); _stylusDoubleTapDelta = ( obj == null ) ? _stylusDoubleTapDelta : (Int32)obj; // The default double tap distance is 15 pixels (value is given in pixels) obj = stylusKey.GetValue( "DblTime" ); _stylusDoubleTapDeltaTime = ( obj == null ) ? _stylusDoubleTapDeltaTime : (Int32)obj; // The default double tap timeout is 800ms obj = stylusKey.GetValue( "Cancel" ); _cancelDelta = ( obj == null ) ? _cancelDelta : (Int32)obj; // The default move delta is 40 (4mm) } touchKey = Registry.CurrentUser.OpenSubKey( "Software\\Microsoft\\Wisp\\Touch" ); if( touchKey != null ) { obj = touchKey.GetValue( "TouchModeN_DtapDist" ); // min = 70%; max = 130%, these values are taken from //depot/winblue_gdr/drivers/tablet/platform/pen/inteng/core/TapsParameterizer.cpp _touchDoubleTapDelta = ( obj == null ) ? _touchDoubleTapDelta : FitToCplCurve( _touchDoubleTapDelta * DoubleTapMinFactor, _touchDoubleTapDelta, _touchDoubleTapDelta * DoubleTapMaxFactor, (Int32)obj ); obj = touchKey.GetValue( "TouchModeN_DtapTime" ); _touchDoubleTapDeltaTime = ( obj == null ) ? _touchDoubleTapDeltaTime : FitToCplCurve( _touchDoubleTapDeltaTime * DoubleTapMinFactor, _touchDoubleTapDeltaTime, _touchDoubleTapDeltaTime * DoubleTapMaxFactor, (Int32)obj ); } } finally { RegistryPermission.RevertAssert(); if( stylusKey != null ) { stylusKey.Close(); } if( touchKey != null ) { touchKey.Close(); } } } /// <summary> /// Fit to control panel controlled curve. 0 matches min, 50 - default, 100 - max /// (the curve is 2 straight line segments connecting the 3 points) /// </summary> private int FitToCplCurve( double vMin, double vMid, double vMax, int value ) { if( value < 0 ) { return (int)vMin; } if( value > 100 ) { return (int)vMax; } double f = (double)value / 100.0; double v = Math.Round( f <= 0.5 ? vMin + 2.0 * f * ( vMid - vMin ) : vMid + 2.0 * ( f - 0.5 ) * ( vMax - vMid ) ); return (int)v; } }
レジストリ情報読み取ってるだけかな?
WindowsForms DataGridView コントロール上でのタッチ操作について
Windows FormsのDataGridViewはタッチ操作(フリック、スワイプっていうらしい)でのスクロールに不具合があります。
もうWindows Formsのバグは修正してくれないので回避コードでいくしかないようです。
DataGridView コントロール上でのタッチ操作について – Visual Studio サポート チーム blog
回避コード
private const int WM_VSCROLL = 0x0115; private const int SB_THUMBPOSITION = 0x0004; private const int SB_THUMBTRACK = 0x0005; [System.Runtime.InteropServices.DllImport("user32")] private static extern int PostMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam); public class MyDataGridView : DataGridView { // メッセージを処理します。 protected override void WndProc(ref Message m){ base.WndProc(ref m); if (m.Msg == WM_VSCROLL){ if (LoWord((long)m.WParam) == LoWord((long)SB_THUMBPOSITION)){ BeginInvoke((Action<IntPtr, IntPtr>)((WParam, LParam) => { // SB_THUMBPOSITION を SB_THUMBTRACK に変更します。 IntPtr testWParam = new IntPtr(SB_THUMBTRACK); // WM_VSCROLL メッセージを再送します。 PostMessage(this.Handle, WM_VSCROLL, testWParam, LParam); }), m.WParam, m.LParam); } } } }
WindowsFormsでタッチ操作は正直限界だと思う。
そしてこの一文
※ 上記回避方法については、お客様において十分にご確認、ご検証くださいますようお願いします。
バグ直せや
参考URL:
Windows 8 でタッチ操作でグリッドをスクロールしたときに描画がズレる。(最後が隠れる) - Microsoft.NET - Project Group
Windows8でデータグリッドビューをタッチ操作するとスクロールバーと描画内容がずれる
Windows8 対応タッチパネルでDataGridViewコントロールによるタッチ操作でのMoseMoveイベントが発生しない。 | Microsoft Connect
[WPF] Imageがぼやける問題
Image を使って画像を表示させていると等倍で表示しているはずなのにぼやけてしまいました。
SnapsToDevicePixels や UseLayoutRounding では直らなかったので途方に暮れていたらいいものがありました。
専用のコントロールを作っているので、Imageコントロールに移植します。
※2016/06/03 LayoutUpdatedイベントの処理を追記
using System; using System.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; public class Image : System.Windows.Controls.Image { public Image() { this.LayoutUpdated += Image_LayoutUpdated; } private Point _pixelOffset; void Image_LayoutUpdated( object sender, EventArgs e ) { Point pixelOffset = GetPixelOffset(); if( !AreClose( pixelOffset, _pixelOffset ) ) { InvalidateVisual(); } } private bool AreClose( Point point1, Point point2 ) { return AreClose( point1.X, point2.X ) && AreClose( point1.Y, point2.Y ); } private bool AreClose( double value1, double value2 ) { if( value1 == value2 ) { return true; } double delta = value1 - value2; return ( ( delta < 1.53E-06 ) && ( delta > -1.53E-06 ) ); } protected override Size MeasureOverride( Size constraint ) { // ちょっとだけ修正 BitmapSourceの時だけ処理する var bitmapSource = Source as BitmapSource; if( bitmapSource != null ) { Size measureSize = new Size(); PresentationSource ps = PresentationSource.FromVisual( this ); if( ps != null ) { Matrix fromDevice = ps.CompositionTarget.TransformFromDevice; Vector pixelSize = new Vector( bitmapSource.PixelWidth, bitmapSource.PixelHeight ); Vector measureSizeV = fromDevice.Transform( pixelSize ); measureSize = new Size( measureSizeV.X, measureSizeV.Y ); } return measureSize; } return base.MeasureOverride( constraint ); } protected override void OnRender( System.Windows.Media.DrawingContext dc ) { _pixelOffset = GetPixelOffset(); dc.DrawImage( this.Source, new Rect( _pixelOffset, DesiredSize ) ); } private Point GetPixelOffset() { Point pixelOffset = new Point(); PresentationSource ps = PresentationSource.FromVisual( this ); if( ps != null ) { Visual rootVisual = ps.RootVisual; // Transform (0,0) from this element up to pixels. pixelOffset = this.TransformToAncestor( rootVisual ).Transform( pixelOffset ); pixelOffset = ApplyVisualTransform( pixelOffset, rootVisual, false ); pixelOffset = ps.CompositionTarget.TransformToDevice.Transform( pixelOffset ); // Round the origin to the nearest whole pixel. pixelOffset.X = Math.Round( pixelOffset.X ); pixelOffset.Y = Math.Round( pixelOffset.Y ); // Transform the whole-pixel back to this element. pixelOffset = ps.CompositionTarget.TransformFromDevice.Transform( pixelOffset ); pixelOffset = ApplyVisualTransform( pixelOffset, rootVisual, true ); // エラーが出るので nullの時の処理を追記 if( rootVisual.TransformToDescendant( this ) != null ) { pixelOffset = rootVisual.TransformToDescendant( this ).Transform( pixelOffset ); } } return pixelOffset; } private Point TryApplyVisualTransform( Point point, Visual v, bool inverse, bool throwOnError, out bool success ) { success = true; if( v != null ) { Matrix visualTransform = GetVisualTransform( v ); if( inverse ) { if( !throwOnError && !visualTransform.HasInverse ) { success = false; return new Point( 0, 0 ); } visualTransform.Invert(); } point = visualTransform.Transform( point ); } return point; } private Point ApplyVisualTransform( Point point, Visual v, bool inverse ) { bool success = true; return TryApplyVisualTransform( point, v, inverse, true, out success ); } private Matrix GetVisualTransform( Visual v ) { if( v != null ) { Matrix m = Matrix.Identity; Transform transform = VisualTreeHelper.GetTransform( v ); if( transform != null ) { Matrix cm = transform.Value; m = Matrix.Multiply( m, cm ); } Vector offset = VisualTreeHelper.GetOffset( v ); m.Translate( offset.X, offset.Y ); return m; } return Matrix.Identity; } }
Visual Studio Installer 編集の自動化
Visual Studio Installer - kitunechan’s blog の続き
毎回Orcaを使って編集するのはめんどくさいし、”よく忘れます”← ここ重要
コマンドラインやバッチファイル的なものはないかと探したらありました。
vbsを使う方法が一番簡単そうです。
MSIファイルのWindows Installerデータベースをプログラムで編集する: .NET Tips: C#, VB.NET
以下の内容でvbsファイルを作成します。
[アドバタイズショートカットから通常のショートカットに切り替える]
NormalShortCut.vbs
Option Explicit Const msiOpenDatabaseModeTransact = 1 Dim msiPath : msiPath = Wscript.Arguments(0) Dim installer Set installer = Wscript.CreateObject("WindowsInstaller.Installer") Dim database Set database = installer.OpenDatabase(msiPath, msiOpenDatabaseModeTransact) Dim query query = "INSERT INTO Property(Property, Value) VALUES('DISABLEADVTSHORTCUTS', '1')" Dim view Set view = database.OpenView(query) view.Execute database.Commit
あとはこのファイルにmsiファイルを渡すだけです。
何かのウインドウが開くこともなく、msiファイルが書き換えられます。
Visual StudioでPostBuildEventに以下のコードを入力して自動化します。
"[パス]\NormalShortCut.vbs" "$(BuiltOuputPath)"
コードだけでlog4netを使う
ログ出力のライブラリとしてlog4netやらNLogが人気があります。
紹介しているサイトを見るとすぐXMLの設定を書かせたがるのですが、後から変更するのか?と。
ということで、log4netをコードから設定する方法です。
ログをファイル出力します。
// 大体一つを使いまわすので static readonly で作る // GetLogger内の文字列は好きなのでいい public static readonly log4net.ILog logger = log4net.LogManager.GetLogger( "Log" ); // コンストラクタ的なところで設定を記述する public App() { // 出力先のAppenderを作成します // ファイル出力で、日付やファイルサイズで新規ファイルを作成するタイプ var Appender = new log4net.Appender.RollingFileAppender() { // 出力するファイル名 File = "log.log", // ファイル追記モード (Falseだと上書き) AppendToFile = true, // ログファイル名を固定にする StaticLogFileName = true, // ログファイル名のローテーション番号の順番 CountDirection = 1, // ログファイルの最大世代 MaxSizeRollBackups = 1, // 文字コード指定 Encoding = System.Text.Encoding.UTF8, // ログを新規ファイルに切替える条件 RollingStyle = log4net.Appender.RollingFileAppender.RollingMode.Size, // 最大ファイルサイズ MaxFileSize = 3072000, //ログのフォーマット Layout = new log4net.Layout.PatternLayout(@"%d [%t] %-5p %type - %m%n"), }; // 記述した設定を有効にする 忘れないように! Appender.ActivateOptions(); var log = (log4net.Repository.Hierarchy.Logger)logger.Logger ; // 出力レベルの設定 デフォルトはALLなのでコメントアウトしちゃう //log.Level = log4net.Core.Level.All; // 出力先を追加します log.AddAppender( Appender ); // 設定を有効にする 忘れないように! log.Hierarchy.Configured = true; }
後は好きなところでメッセージ出力して下さい。
logger.Debug( "Debug" ); logger.Info( "Info" ); logger.Warn( "Warn" ); logger.Error( "Error" ); logger.Fatal( "Fatal" );
参考:
log4net の Appender を プログラム上で動的に 生成、追加 する 方法
c# - How to configure NHibernate logging with log4net in code, not in xml file? - Stack Overflow
Log4Net設定 MAXファイルサイズ及び日単位で切替え | Wait Cursor
Log4net(6) RollingFileAppender 意味不明パラメータ:CountDirection - shima111の日記
C# .Net アプリケーションで複数のアイコンを登録する方法
C# .Net のアプリケーションでショートカットアイコン用に複数のアイコンファイルをEXEに入れたかったのですが、
Visual Studio 2013だけではできませんでした。
参考イメージ
ショートカット アイコンの変更 - Google 検索
では本題です。
長いので目次付けました。
- そもそもアイコンを設定する方法
- Win32リソースを作成して複数のアイコンを登録する方法
- Resource Hackerを使って後から複数のアイコンを追加する方法
- InsertIconsを使って後から複数のアイコンを追加する方法
おすすめは最後の「InsertIconsを使って後から複数のアイコンを追加する方法」です。
続きを読むVisual Studio Installer Projects
これのお話
Microsoft Visual Studio 2015 Installer Projects - Visual Studio Marketplace
ほぼ覚書
UpgradeCode を一致させておくとインストールプログラム一覧にダブらずにインストールできる。
ProductCode は必ず変更すること。
ファイルのPermanentをTrueにしておくとアンインストール時にも削除されない。
インストーラーのバージョンは作成時はどうあがいても3桁(1.0.0)でしかビルドできない。
ビルド後にOrcaを使えば変更可能。
定義されているプロパティ一覧
Property Reference - Windows applications | Microsoft Docs
[WindowsVolume]や[ProductVersion]とか
アイコンはそれぞれ設定する必要がある
・ショートカットファイル
・インストールプログラム一覧
作成されるショートカットがアドバタイズショートカットというタイプなので通常のショートカットに切り替える必要がある。
OrcaでMSIファイルを開き、PropertyテーブルにProperty「DISABLEADVTSHORTCUTS」、Value「1」の行を新たに追加する。
アドバタイズショートカットではなく、普通のショートカットを作成する - .NET Tips (VB.NET,C#...)
Orcaは以下の場所にインストーラーが保存されている
C:\Program Files\Microsoft SDKs\Windows\[バージョン]\Bin\Orca.exe
Program Files (x86)ではないので注意。まあ、両方見よう
WPFで画像処理中にメモリが開放されず・・・
覚書程度に記載です
WPFで Canvas(正確にはVisualクラス?) を RenderTargetBitmap を使って大量に画像出力していたら
エラー「メモリが足りません」
System.OutOfMemoryException: プログラムの実行を続行するための十分なメモリがありませんでした。
はー悲しい。。。
こんなのループしました。
※ブログ用に書いたから動かないかも
// 画像の読み込み var bmp = new BitmapImage(new Uri( "ファイル名をいれてね" )); // 画像拡大率の指定 var Scale = 1.5D; for ( var i = 0 ; i < 1000; i++){ var canvas = new Canvas() { SnapsToDevicePixels = true, Children = { ( new Func<UIElement>( ()=>{ var re = new Image() { Source = bmp, LayoutTransform = new ScaleTransform( Scale, Scale )//画像拡大 }; RenderOptions.SetBitmapScalingMode( re, BitmapScalingMode.HighQuality); return re; }))(), }, }; canvas.Measure( new System.Windows.Size( bmp.Width * Scale, bmp.Height * Scale ) ); canvas.Arrange( new Rect( 0, 0, bmp.Width * Scale, bmp.Height * Scale ) ); canvas.UpdateLayout(); // ファイル書き出し var r = new RenderTargetBitmap( (int)canvas.ActualWidth, (int)canvas.ActualHeight, 96, 96, PixelFormats.Default ); r.Render( canvas ); var img = new PngBitmapEncoder() { Frames = { BitmapFrame.Create( r ) }, }; using( var stream = new FileStream( "./"+i+".png", FileMode.Create ) ) { img.Save( stream ); } // 強制メモリ解放!!! GC働けええええ // GC.Collect(); // GC.WaitForPendingFinalizers(); // GC.Collect(); }
メソッドを抜けるまでガンガンメモリが増えていくやつです。
メモリリークしているわけじゃなくてGC君が「後でやるよ」をし続けた結果「メモリが足りません」の流れっぽいです。
ただし GC.Collect(); だけでは足りないみたいで GC.WaitForPendingFinalizers(); の必要がありました。
こんな感じ?
GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();