![](/img/trans.png)
[英]Add all items inside ListView ObservableCollection in One Shot (WPF Mvvm)
[英]WPF MVVM Binding of itemssource to an ObservableCollection inside a different class and add items to it
我已經將一個小應用程序與控制台 window 集成在一起,將信息傳遞給用戶。
在 meiner view habe ich ein ItemControl 和 im Template ein TextBlock 中,welches den "ConsoleText" anzeigt in der gewünschten Farbe "FontColour":
<Window x:Class="MyApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:helper="clr-namespace:MyApplication.Helpers"
mc:Ignorable="d"
Title="MainWindow" Height="600" Width="800" MinHeight="600" MinWidth="800">
<Grid>
<DockPanel Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="3" Grid.RowSpan="4" Background="Black">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" helper:ScrollViewerExtension.AutoScroll="True">
<ItemsControl ItemsSource="{Binding logger.ConsoleOutput}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ConsoleText}" Foreground="{Binding FontColour}" FontSize="14"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</DockPanel>
</Grid>
</Window>
Mein CodeBehind beinhaltet lediglich den DataContext:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainWindowViewModel();
}
}
我的視圖的 ViewMode 具有三個信息:
這里是 ViewModel:
public class MainWindowViewModel : ObservableObject
{
MyLogicClass myLogicClass = new MyLogicClass(null);
ConsoleOutputList logger = new ConsoleOutputList();
public MainWindowViewModel()
{
MyButtonCommand = new RelayCommand(o => Task.Run(() => myLogicClass.Test()));
}
public ICommand MyButtonCommand { get; }
}
ConsoleOutputList 包含來自“ConsoleLine”(也在另一個項目中)的 ObservableCollection。 “ConsoleLine”在構造函數中可能有一個字符串和一個 SolidColorBrush(我認為這也不相關)。
public class ConsoleOutputList : ObservableObject
{
public ConsoleOutputList()
{
ConsoleOutput = new ObservableCollection<ConsoleLine>();
// For testing purposes I add a random entry to see if the binding in general works - but this doesn`t work neither
ConsoleOutput.Add(new ConsoleLine("Test", Brushes.Green));
}
public ObservableCollection<ConsoleLine> consoleOutput { get; set; }
public ObservableCollection<ConsoleLine> ConsoleOutput
{
get
{
return consoleOutput;
}
set
{
consoleOutput = value;
}
}
//Used to add new lines to the ObservableCollection
public void WriteToConsole(object msg, SolidColorBrush fontColour)
{
ConsoleOutput.Add(new ConsoleLine(msg, fontColour));
}
}
此類用於所有應用程序邏輯(也在另一個項目中)。 作為測試,我在這里使用方法 Test() 來簡單地添加文本。
public class MyLogicClass
{
ConsoleOutputList Logger = new ConsoleOutputList();
public void Test()
{
Logger.WriteToConsole($"Test", Brushes.Gray);
}
}
現在我有兩個問題:
是的,我知道 - 我創建了兩個 ConsoleOutputList 實例,這是不正確的(這是第二個問題的原因)。 但我不知道如何做得更好,因為我需要從代碼中的任何地方訪問 WriteToConsole()。 (也許更改為 static?)但是我如何解決第一個問題以及它如何與 static 屬性一起使用並將它們顯示在視圖中。
更新:即使我將所有內容更改為 static,“測試”行顯示為綠色,但我之后添加的所有內容都未顯示在 GUI 中: Visual Studio
在 WPF 中,您無法綁定到方法或字段。 您必須綁定到公共屬性(請參閱Microsoft Docs:綁定源類型以了解有關支持的綁定源的更多信息)。
要解決問題 1),您必須將MainWindowViewModel.logger
字段實現為公共屬性。 如果預期屬性會更改,則它必須引發INotifyPropertyChanged.PropertyChanged
事件。
要修復 2),您必須在整個應用程序中分發ConsoleOutputList
的共享實例。 將其公開為 static 實例是一種解決方案,但通常不推薦。 更好的解決方案是將共享實例傳遞給依賴於ConsoleOutputList
的每種類型的構造函數。
固定的解決方案可能如下所示:
MainWindowViewModel.cs
public class MainWindowViewModel : ObservableObject
{
public MainWindowViewModel(ConsoleOutputList logger, MyLogicClass myLogicClass)
{
this.Logger = logger;
this.MyLogicClass = myLogicClass;
this.MyButtonCommand = new RelayCommand(o => Task.Run(() => myLogicClass.Test()));
}
public ConsoleOutputList Logger { get; }
public ICommand MyButtonCommand { get; }
private MyLogicClass MyLogicClass { get; }
}
我的邏輯類.cs
public class MyLogicClass
{
private ConsoleOutputList Logger { get; }
public MyLogicClass(ConsoleOutputList logger) => this.Logger = logger;
public void Test()
{
this.Logger.WriteToConsole($"Test", Brushes.Gray);
}
}
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow(MainWindowViewModel mainWindowViewModel)
{
InitializeComponent();
this.DataContext = mainWindowViewModel;
}
}
申請.xaml
<Application Startup="App_OnStartup">
</Application>
應用程序.xaml.cs
public partial class App : Application
{
private void App_OnStartup(object sender, StartupEventArgs e)
{
/** Initialize the application with the shared instance **/
var sharedLoggerInstance = new ConsoleOutputList();
var classThatNeedsLogger = new MyLogicClass(sharedLoggerInstance);
var mainViewModel = new MainWindowViewModel(sharedLoggerInstance, classThatNeedsLogger);
var mainWindow = new MainWindow(mainViewModel);
mainWindow.Show();
}
}
也不ItemsControl
包裝到ScrollViewer
中。 請改用ListBox
。 ListBox
是一個增強的ItemsControl
,默認啟用ScrollViewer
和 UI 虛擬化。 如果您希望生成許多日志條目,您最終會在列表中看到許多項目。 如果您不使用 UI 虛擬化,您的ItemsControl
將破壞 GUI 的性能/響應能力。
<DockPanel>
<ListBox ItemsSource="{Binding Logger.ConsoleOutput}">
<ListBox.ItemTemplate>
...
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
要允許從后台線程或一般的非 UI 線程更新集合ConsoleOutput
,您可以使用Dispatcher
更新集合(在這種情況下不推薦):
Dispatcher.InvokeAsync(() => myCollection.Add(item));
或配置綁定引擎以將集合的CollectionChanged
事件編組到調度程序線程。
由於關鍵的 object 是一個作為綁定源的集合,我建議通過調用 static BindingOperations.EnableCollectionSynchronization
方法來配置綁定引擎。 必須在調度程序線程上調用該方法:
控制台輸出列表.cs
public class ConsoleOutputList : ObservableObject
{
private object SyncLock { get; }
public ConsoleOutputList()
{
this.SyncLock = new object();
this.ConsoleOutput = new ObservableCollection<ConsoleLine>();
// Configure the binding engine to marshal the CollectionChanged event
// of this collection to the UI thread to prevent cross-thread exceptions
BindingOperations.EnableCollectionSynchronization(this.ConsoleOutput, this.SyncLock);
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.