ListView の表形式表示で改行があると
◆ 改行が表示されて表の 1 行に複数行表示される
◆ 見づらいので Converter 使って 1 行に変換する
◆ 複数行になる可能性ある列は多いと思うので Converter を Binding ごとに書くのは面倒
◆ → ユーザコントロール作る
◆ 見づらいので Converter 使って 1 行に変換する
◆ 複数行になる可能性ある列は多いと思うので Converter を Binding ごとに書くのは面倒
◆ → ユーザコントロール作る
ListView で GridView を使って表形式 (エクスプローラの詳細表示みたいの) で表示するときに テキスト中に改行があると そのまま表示されて表の 1 行の高さが揃わなくなります
[Window1.xaml]
[Window1.xaml.cs]
こんな感じで binding してるデータに \n を含めると

HTML みたいに何もしないと改行は無視されて 明示的に改行を反映するように書かないと (white-space: pre-wrap; とか) 改行されて表示されないと思ってたのに普通に表示されていました
表示するデータの元はユーザ入力が多いと思うので 何も対策しないと入力によって見づらい事になっていまう可能性があります
特に 1 行の高さが画面の高さ以上になるとホイールでのスクロールで一気進んでしまったり 操作もまともにできなくなって使いものになりません
スクロール問題だけなら ListView に
DisplayMemberBinding に代わりに DataTemplate を使って内部を自分で書いて TextBlock に NoWrap を指定してます
結果は効果なし
NoWrap は端までいったときの自動改行のオプションなので明示的な改行文字があれば改行されます
改行を半角空白に置換する Converter を作ります
Binding にこの Converter を使います
Window.Resources でリソース定義して DisplayMemberBinding の Converter でリソースを指定しています
これで 複数行あっても 1 行で表示されるようになりました

自動でコンバータを通してくれるユーザコントロールを作ってもう少し楽にします
自動でコンバータを通す場合に もともとコンバータが指定されていた場合に順番にコンバータ通すようなコンバータが必要です
なのでこういうコンバータを作りました
プロパティ converters に IValueConverter のリストを入れておいて Convert などが呼ばれたら順番に IValueConverter を通します
次に ユーザコントロールです
すごくシンプルで GridViewColumn を継承して DisplayMemberBinding を再定義しています
set されたときに SingleLineConverter をセットして親クラスの DisplayMemberBinding にセットします
get はそのまま親クラスのものを返します
すでに何かのコンバータがあるときは 上で作ったコンバータを順に通すコンバータを使って 最後に SingleLineConverter を通すようにセットします
これで使うときには
これだけで済むようになりました
複数行のテキストが来る可能性あるところは GridViewColumn の代わりに SingleLineGridViewColumn を使うだけで大丈夫です
[Window1.xaml]
<Window x:Class="w01.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:w01"
mc:Ignorable="d"
Title="Window1" Height="300" Width="300">
<Grid>
<ListView ItemsSource="{Binding items}">
<ListView.View>
<GridView>
<GridViewColumn Header="名前" Width="100" DisplayMemberBinding="{Binding text}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:w01"
mc:Ignorable="d"
Title="Window1" Height="300" Width="300">
<Grid>
<ListView ItemsSource="{Binding items}">
<ListView.View>
<GridView>
<GridViewColumn Header="名前" Width="100" DisplayMemberBinding="{Binding text}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
[Window1.xaml.cs]
namespace w01
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
this.DataContext = new
{
items = new[]
{
new { text = "1行", },
new { text = "2\n行", },
new { text = "あ\nい\nう\nえ\nお", },
}
};
}
}
}
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
this.DataContext = new
{
items = new[]
{
new { text = "1行", },
new { text = "2\n行", },
new { text = "あ\nい\nう\nえ\nお", },
}
};
}
}
}
こんな感じで binding してるデータに \n を含めると

HTML みたいに何もしないと改行は無視されて 明示的に改行を反映するように書かないと (white-space: pre-wrap; とか) 改行されて表示されないと思ってたのに普通に表示されていました
表示するデータの元はユーザ入力が多いと思うので 何も対策しないと入力によって見づらい事になっていまう可能性があります
特に 1 行の高さが画面の高さ以上になるとホイールでのスクロールで一気進んでしまったり 操作もまともにできなくなって使いものになりません
スクロール問題だけなら ListView に
ScrollViewer.CanContentScroll="False"
を設定すればいいのですが 表の 1 行に複数行のテキストがある時点でみづらいので 1 行になるようにしたいですNoWrap?
単純に NoWrap でできないかなという希望でこんなことしてみました<GridViewColumn Header="名前" Width="100">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding text}" TextWrapping="NoWrap"></TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>v
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding text}" TextWrapping="NoWrap"></TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>v
DisplayMemberBinding に代わりに DataTemplate を使って内部を自分で書いて TextBlock に NoWrap を指定してます
結果は効果なし
NoWrap は端までいったときの自動改行のオプションなので明示的な改行文字があれば改行されます
SingleLineConverter
スタイルではどうにもできなさそうなので仕方なく Converter を使って View 側の表示時に変換を行います改行を半角空白に置換する Converter を作ります
public class SingleLineConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType != typeof(string) || !(value is string))
{
throw new InvalidOperationException("Only supports converting between strings.");
}
return Regex.Replace((string)value, "\r\n|\r|\n", " ", RegexOptions.ECMAScript);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType != typeof(string) || !(value is string))
{
throw new InvalidOperationException("Only supports converting between strings.");
}
return Regex.Replace((string)value, "\r\n|\r|\n", " ", RegexOptions.ECMAScript);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
Binding にこの Converter を使います
<Window x:Class="w01.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:w01"
mc:Ignorable="d"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<local:SingleLineConverter x:Key="SingleLineConverter" />
</Window.Resources>
<Grid>
<ListView ItemsSource="{Binding items}" ScrollViewer.CanContentScroll="False">
<ListView.View>
<GridView>
<GridViewColumn Header="名前" Width="100" DisplayMemberBinding="{Binding text,Converter={StaticResource SingleLineConverter}}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:w01"
mc:Ignorable="d"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<local:SingleLineConverter x:Key="SingleLineConverter" />
</Window.Resources>
<Grid>
<ListView ItemsSource="{Binding items}" ScrollViewer.CanContentScroll="False">
<ListView.View>
<GridView>
<GridViewColumn Header="名前" Width="100" DisplayMemberBinding="{Binding text,Converter={StaticResource SingleLineConverter}}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
Window.Resources でリソース定義して DisplayMemberBinding の Converter でリソースを指定しています
これで 複数行あっても 1 行で表示されるようになりました

SingleLineGridViewColumn
1 行で表示できるようになったのはいいのですが 毎回コンバータを書くのは面倒ですよね自動でコンバータを通してくれるユーザコントロールを作ってもう少し楽にします
自動でコンバータを通す場合に もともとコンバータが指定されていた場合に順番にコンバータ通すようなコンバータが必要です
なのでこういうコンバータを作りました
public class SequentialConverter : IValueConverter
{
public List<IValueConverter> converters { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return converters.Aggregate(value, (acc, converter) =>
{
return converter.Convert(acc, targetType, parameter, culture);
});
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return converters.Reverse<IValueConverter>().Aggregate(value, (acc, converter) =>
{
return converter.Convert(acc, targetType, parameter, culture);
});
}
}
{
public List<IValueConverter> converters { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return converters.Aggregate(value, (acc, converter) =>
{
return converter.Convert(acc, targetType, parameter, culture);
});
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return converters.Reverse<IValueConverter>().Aggregate(value, (acc, converter) =>
{
return converter.Convert(acc, targetType, parameter, culture);
});
}
}
プロパティ converters に IValueConverter のリストを入れておいて Convert などが呼ばれたら順番に IValueConverter を通します
次に ユーザコントロールです
public class SingleLineGridViewColumn : GridViewColumn
{
public new Binding DisplayMemberBinding
{
get { return (Binding)base.DisplayMemberBinding; }
set
{
if(value.Converter == null)
{
value.Converter = new SingleLineConverter();
}
else
{
value.Converter = new SequentialConverter()
{
converters = new List<IValueConverter>
{
value.Converter,
new SingleLineConverter(),
}
};
}
base.DisplayMemberBinding = value;
}
}
}
{
public new Binding DisplayMemberBinding
{
get { return (Binding)base.DisplayMemberBinding; }
set
{
if(value.Converter == null)
{
value.Converter = new SingleLineConverter();
}
else
{
value.Converter = new SequentialConverter()
{
converters = new List<IValueConverter>
{
value.Converter,
new SingleLineConverter(),
}
};
}
base.DisplayMemberBinding = value;
}
}
}
すごくシンプルで GridViewColumn を継承して DisplayMemberBinding を再定義しています
set されたときに SingleLineConverter をセットして親クラスの DisplayMemberBinding にセットします
get はそのまま親クラスのものを返します
すでに何かのコンバータがあるときは 上で作ったコンバータを順に通すコンバータを使って 最後に SingleLineConverter を通すようにセットします
これで使うときには
<Window x:Class="w01.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:w01"
mc:Ignorable="d"
Title="Window1" Height="300" Width="300">
<Grid>
<ListView ItemsSource="{Binding items}" ScrollViewer.CanContentScroll="False">
<ListView.View>
<GridView>
<local:SingleLineGridViewColumn Header="名前" Width="100" DisplayMemberBinding="{Binding text}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:w01"
mc:Ignorable="d"
Title="Window1" Height="300" Width="300">
<Grid>
<ListView ItemsSource="{Binding items}" ScrollViewer.CanContentScroll="False">
<ListView.View>
<GridView>
<local:SingleLineGridViewColumn Header="名前" Width="100" DisplayMemberBinding="{Binding text}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
これだけで済むようになりました
複数行のテキストが来る可能性あるところは GridViewColumn の代わりに SingleLineGridViewColumn を使うだけで大丈夫です