Ok。winformsを忘れてください。役に立たず、非推奨で、見苦しく、カスタマイズができず、UI の仮想化とハードウェア レンダリングが不足しているため、非常に遅いです。
これはあなたが説明したことに対する私の見解です:
<Window x:Class="MiscSamples.ThreeColumnChatSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MiscSamples"
Title="ThreeColumnChatSample" Height="300" Width="300">
<Window.Resources>
<local:FlowDocumentToXamlConverter x:Key="DocumentConverter"/>
</Window.Resources>
<ListView ItemsSource="{Binding}" ScrollViewer.HorizontalScrollBarVisibility="Hidden">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn DisplayMemberBinding="{Binding DateTime}"/>
<GridViewColumn DisplayMemberBinding="{Binding Sender}"/>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<FlowDocumentScrollViewer Document="{Binding Content, Converter={StaticResource DocumentConverter}}"
VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Hidden"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
</Window>
コードビハインド:
public partial class ThreeColumnChatSample : Window
{
public ObservableCollection<ChatEntry> LogEntries { get; set; }
private string TestData = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum";
private List<string> words;
private int maxword;
public Random random { get; set; }
public ThreeColumnChatSample()
{
InitializeComponent();
random = new Random();
words = TestData.Split(' ').ToList();
maxword = words.Count - 1;
DataContext = LogEntries = new ObservableCollection<ChatEntry>();
Enumerable.Range(0, 100)
.ToList()
.ForEach(x => LogEntries.Add(GetRandomEntry()));
}
private ChatEntry GetRandomEntry()
{
return new ChatEntry()
{
DateTime = DateTime.Now,
Sender = words[random.Next(0, maxword)],
Content = GetFlowDocumentString(string.Join(" ",Enumerable.Range(5, random.Next(10, 50)).Select(x => words[random.Next(0, maxword)])))
};
}
private string GetFlowDocumentString(string text)
{
return "<FlowDocument xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>" +
" <Paragraph>" +
" <Run Text='" + text + "'/>" +
" </Paragraph>" +
"</FlowDocument>";
}
}
データ項目:
public class ChatEntry:PropertyChangedBase
{
public DateTime DateTime { get; set; }
private string _content;
public string Content
{
get { return _content; }
set
{
_content = value;
OnPropertyChanged("Content");
}
}
public string Sender { get; set; }
}
PropertyChangedBase (MVVM ヘルパー クラス):
public class PropertyChangedBase:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
Application.Current.Dispatcher.BeginInvoke((Action) (() =>
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}));
}
}
結果:

- この投稿の を使用しまし
FlowDocumentToXAMLConverter
た
- 3 列目のリッチ コンテンツは で示されていますが、リンクされた投稿から
FlowDocumentViewer
バインド可能なものを使用するように変更できます。RichTextBox
- ヘッダーの端をクリックしてドラッグすると、列のサイズを変更できます。
- WPF には UI の仮想化が組み込まれているため、大量の行がある場合でも、アプリケーションがひどく遅れることはありません。
- ここで説明するソリューションを実装して、含まれているウィンドウのサイズを変更するときに最後の列のサイズを変更することができます。これにより、ワードラップと解像度の独立性が実現されます。
- コード ビハインドのほとんどは、実際には例をサポートするためのボイラープレートであることに注意してください (ランダム エントリの生成など)。それを取り除くと、本当にきれいな解決策になります。
- WPF ロックス。私のコードを(リンクされた投稿のコンバーターと一緒に)コピーして貼り付けて
File -> New Project -> WPF Application
、結果を自分で確認してください。
編集:
@KingKing の要求に従って、サンプルを変更してチャット クライアントをエミュレートしました。
FsRichTextBox.dll
上記のリンクされた CodeProject 投稿からの参照を追加しました。
<Window x:Class="MiscSamples.ThreeColumnChatSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MiscSamples"
xmlns:rtb="clr-namespace:FsWpfControls.FsRichTextBox;assembly=FsRichTextBox"
Title="ThreeColumnChatSample" WindowState="Maximized">
<Window.Resources>
<local:FlowDocumentToXamlConverter x:Key="DocumentConverter"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="300"/>
</Grid.RowDefinitions>
<ListView ItemsSource="{Binding ChatEntries}" ScrollViewer.HorizontalScrollBarVisibility="Hidden"
x:Name="ListView">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn DisplayMemberBinding="{Binding DateTime}"/>
<GridViewColumn DisplayMemberBinding="{Binding Sender}"/>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<FlowDocumentScrollViewer Document="{Binding Content, Converter={StaticResource DocumentConverter}}"
VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Hidden"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
<GridSplitter Height="3" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Top"/>
<DockPanel Grid.Row="1">
<Button Content="Send" DockPanel.Dock="Right" VerticalAlignment="Bottom" Margin="2"
Click="Send_Click"/>
<rtb:FsRichTextBox Document="{Binding UserInput,Converter={StaticResource DocumentConverter}, Mode=TwoWay}"
DockPanel.Dock="Bottom" Height="300" x:Name="InputBox"/>
</DockPanel>
</Grid>
</Window>
コードビハインド:
public partial class ThreeColumnChatSample : Window
{
public ChatViewModel ViewModel { get; set; }
public ThreeColumnChatSample()
{
InitializeComponent();
DataContext = ViewModel = new ChatViewModel();
}
private void Send_Click(object sender, RoutedEventArgs e)
{
InputBox.UpdateDocumentBindings();
var entry = ViewModel.AddEntry();
ListView.ScrollIntoView(entry);
}
}
ビューモデル:
public class ChatViewModel:PropertyChangedBase
{
public ObservableCollection<ChatEntry> ChatEntries { get; set; }
private string _userInput;
public string UserInput
{
get { return _userInput; }
set
{
_userInput = value;
OnPropertyChanged("UserInput");
}
}
public string NickName { get; set; }
public ChatViewModel()
{
ChatEntries = new ObservableCollection<ChatEntry>();
NickName = "John Doe";
}
public ChatEntry AddEntry()
{
var entry = new ChatEntry {DateTime = DateTime.Now, Sender = NickName};
entry.Content = UserInput;
ChatEntries.Add(entry);
UserInput = null;
return entry;
}
}
結果:
