場景
應用程式主界面需要顯示程式運作中的log資訊,随着log資訊的增多,應能自動滾動到最新的一條資訊。
針對不同的資訊顯示不同,比如
info
資訊正常顯示,
error
資訊标紅提醒。
由于使用的MVVM架構,希望xaml檔案中隻出現
Binding LogText
,将顯示與邏輯分離。選擇ListBox作為顯示的控件,存在兩個問題:
- 當log資訊長度超過ListBox寬度時,不會自動換行;
- 無法通過設定參數(比如
、
SelectedItem
)使ListBox自動滾動到最下方。
SelectedIndex
ListBox内容自動換行
基本思路:
- TextBlock中存在屬性
可以實作自動換行;TextWrapping = "Wrap"
- 讓TextBlock作為ListBox的item,每一行log綁定到Text屬性中;
- Logger類中要用
作為ListBox的ItemSource。ObservableCollection<T>
ObservableCollection<T>
用法和
List<T>
基本相同,多了當值變化時引發通知的功能。本例中,
T
需要是一個類,才能讓ListBoxItem(即TextBlock)綁定其中的屬性。
// 單條Log記錄
public class Log
{
public bool IsError { get; set; }
public string LogText { get; set; }
}
public class Logger : ObservableObject
{
private ObservableCollection<Log> _logList;
public ObservableCollection<Log> LogList
{
get => _logList;
set => Set(() => LogList, ref _logList, value);
}
}
ViewModel中:
public class LoggerViewModel : ViewModelBase
{
private Logger _log;
public Logger Log
{
get => _log;
set => Set(() => Log, ref _log, value);
}
}
Xaml中:
注意兩個設定:
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
和
TextWrapping="Wrap"
。
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding Log.Logger}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding LogText}" TextWrapping="Wrap" Margin="-5"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
參考:Force TextBlock to wrap in WPF ListBox
ListBox滾動到最下方
基本思路:
- 在WPF中隻有通過
來實作滾動到視區;ScrollIntoView
- 當Logger中的内容發生變化時,應該
,來觸發RaisePropertyChanged
事件。ScrollIntoView
可以用codeBehind、Behavior來實作,但是最簡潔幹淨的實作方式還是重寫一個
ScrollingListBox
控件,繼承自
ListBox
。
public class ScrollingListBox : ListBox
{
protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.NewItems == null) return;
var newItemCount = e.NewItems.Count;
if (newItemCount > 0)
this.ScrollIntoView(e.NewItems[newItemCount - 1]);
base.OnItemsChanged(e);
}
}
在對應的XAML中,使用如下:
xmlns:custom="clr-namespace:ScrollingListBoxNamespace"
<custom:ScrollingListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding Log.Logger}" >
<ListBox.ItemTemplate>
......
</ListBox.ItemTemplate>
</custom:ScrollingListBox>
還可以在App.xmal中設定該ListBox的樣式:
xmlns:custom="clr-namespace:ScrollingListBoxNamespace"
<Application.Resources>
<ResourceDictionary>
<Style TargetType="common:ScrollingListBox" BasedOn="{StaticResource MaterialDesignListBox}"/>
</ResourceDictionary>
</Application.Resources>
參考:ListBox Scroll Into View with MVVM