WPF - 用户控件 - 绑定依赖属性
WPF - UserControl - Binding Dependency Properties
我正在做一个用户控件,以便能够显示日志。我希望用户能够对 select 行进行复制和粘贴。我计划使用 RichTextBox,因为根据日志行的类型(警告、信息、错误),我会更改颜色。
这是代码:
<UserControl x:Class="WpfAppFrameLogging.UserControls.LoggerControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfAppFrameLogging.UserControls"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<RichTextBox x:Name="RichText"
IsReadOnly="True"
ScrollViewer.VerticalScrollBarVisibility="Auto">
<FlowDocument x:Name="FlowDoc">
<FlowDocument.Resources>
<Style TargetType="{x:Type Paragraph}">
<Setter Property="Margin" Value="0"/>
</Style>
</FlowDocument.Resources>
</FlowDocument>
</RichTextBox>
</Grid>
</UserControl>
在那里,我想要一个 ObservableCollection 类型的依赖属性,当我的控制器中添加了一个日志行时,它会在 RichTextBox 中添加一个新段落。
看起来像这样的东西。
代码隐藏用户控件
var paragraphLog = new Paragraph(new Run(textLog));
paragraphLog.Margin = new Thickness(0, 0, 0, 0);
switch (status)
{
case StatusLog.Error:
paragraphLog.Foreground = Brushes.Red;
break;
case StatusLog.Success:
paragraphLog.Foreground = Brushes.Green;
break;
case StatusLog.Warn:
paragraphLog.Foreground = Brushes.Orange;
break;
case StatusLog.Info: // Useless--> I know
default:
paragraphLog.Foreground = Brushes.Black;
break;
}
FlowDoc.Blocks.Add(paragraphLog);
RichText.ScrollToEnd();
Class 日志信息
public class LogInfo
{
public string TextLog { get; set; }
public StatusLog StatusLog { get; set; }
}
public enum StatusLog
{
Info, // --> Black
Warn, // --> Orange
Error, // --> Red
Success // --> Green
}
在视图中,我将使用 UserControl:
<myControls:LoggerControl AllLogsInfo="{Binding MainLogInfos, Mode=OneWay}" />
和MainLogInfos
public ObservableCollection<LogInfo> MainLogInfos
{
get { return _mainLogInfos; }
set
{
if (value != _mainLogInfos)
{
_mainLogInfos = value;
NotifyPropertyChanged();
}
}
}
private ObservableCollection<LogInfo> _mainLogInfos;
// Method :
public void LogError(string message)
{
MainLogInfos.Add(new LogInfo() { TextLog = message, StatusLog = StatusLog.Error });
}
我仅限于 4.7.2 框架。
感谢您的帮助、想法、建议...
此示例使用带有文本框的列表框。它被设置为看起来像一个 TextBlock。 Style 用于根据 StatusLog 设置颜色。我包含了 4 条不同状态的日志消息。您可以 select 多行,我已经包含了一个用于复制 selected 文本的上下文菜单。
MainWindow.xaml
<Window x:Class="SO65185215.MainWindow"
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:SO65185215"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<ListBox ItemsSource="{Binding MainLogInfos}">
<ListBox.Resources>
<Style x:Key="TextBoxStyle" TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding StatusLog}" Value="Warn">
<Setter Property="Foreground" Value="Orange"/>
</DataTrigger>
<DataTrigger Binding="{Binding StatusLog}" Value="Error">
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding StatusLog}" Value="Success">
<Setter Property="Foreground" Value="Green"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox x:Name="tb" Text="{Binding TextLog}"
BorderThickness="0"
Background="Transparent"
IsReadOnly="True"
Style="{StaticResource TextBoxStyle}">
<TextBox.ContextMenu>
<ContextMenu>
<MenuItem Command="ApplicationCommands.Copy"/>
</ContextMenu>
</TextBox.ContextMenu>
</TextBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
MainWindow.xaml.cs
using System.Collections.ObjectModel;
using System.Windows;
namespace SO65185215
{
public partial class MainWindow : Window
{
public MainWindow()
{
DataContext = this;
InitializeComponent();
Loaded += MainWindow_Loaded;
}
public ObservableCollection<LogInfo> MainLogInfos { get; } = new ObservableCollection<LogInfo>();
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
MainLogInfos.Add(new LogInfo() { TextLog = "log message 1", StatusLog = StatusLog.Info });
MainLogInfos.Add(new LogInfo() { TextLog = "log message\nNext line message 2", StatusLog = StatusLog.Error });
MainLogInfos.Add(new LogInfo() { TextLog = "log message3\nWarning", StatusLog = StatusLog.Warn });
MainLogInfos.Add(new LogInfo() { TextLog = "log message success", StatusLog = StatusLog.Success });
}
}
public class LogInfo
{
public string TextLog { get; set; }
public StatusLog StatusLog { get; set; }
}
public enum StatusLog
{
Info, // --> Black
Warn, // --> Orange
Error, // --> Red
Success // --> Green
}
}
编辑:
如果您的目的是 select 多条消息,那么您可以将 ListBox 设置为 Multipel/Extended selectionMode:
<ListBox x:Name="listBox" ItemsSource="{Binding MainLogInfos}" SelectionMode="Multiple">
将上下文菜单移至列表框
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Copy selected" Click="CopySelected_Clicked"/>
</ContextMenu>
</ListBox.ContextMenu>
在 Click 事件处理程序中将 selected 文本连接到剪贴板。
观察 selectedItems 集合包含消息的顺序是 selected。
private void CopySelected_Clicked(object sender, RoutedEventArgs e)
{
StringBuilder sb = new StringBuilder();
foreach(LogInfo item in listBox.SelectedItems)
{
sb.AppendLine(item.TextLog);
}
if (sb.Length > 0)
Clipboard.SetText(sb.ToString());
}
我找到了解决方案
我的LoggerControl.xaml
<UserControl x:Class="SDK.FrontOffice.CustomControl.LoggerControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:SDK.FrontOffice.CustomControl"
xmlns:controls="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
xmlns:converter="clr-namespace:SDK.FrontOffice.Converters"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<!--<Grid.Resources>
<converter:TypeInfoToColorConverter x:Key="converterTypeInfo" />
</Grid.Resources>-->
<!--<GroupBox Header="Logs">-->
<RichTextBox x:Name="RichText"
IsReadOnly="True"
ScrollViewer.VerticalScrollBarVisibility="Auto">
<FlowDocument x:Name="FlowDoc">
<FlowDocument.Resources>
<Style TargetType="{x:Type Paragraph}">
<Setter Property="Margin" Value="0"/>
</Style>
</FlowDocument.Resources>
</FlowDocument>
</RichTextBox>
<!--</GroupBox>-->
</Grid>
</UserControl>
和代码隐藏
/// <summary>
/// Logique d'interaction pour LoggerControl.xaml
/// </summary>
public partial class LoggerControl : UserControl
{
public LoggerControl()
{
InitializeComponent();
}
#region DependencyProperty
public ObservableCollection<LogInfo> AllLogs
{
get { return (ObservableCollection<LogInfo>)GetValue(AllLogsProperty); }
set { SetValue(AllLogsProperty, value); }
}
// Using a DependencyProperty as the backing store for AllLogs. This enables animation, styling, binding, etc...
public static readonly DependencyProperty AllLogsProperty =
DependencyProperty.Register("AllLogs",
typeof(ObservableCollection<LogInfo>),
typeof(LoggerControl),
new PropertyMetadata(new ObservableCollection<LogInfo>(),
OnAllLogsChanged));
private static void OnAllLogsChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
LoggerControl loggerControl = sender as LoggerControl;
var old = e.OldValue as ObservableCollection<LogInfo>;
if (old != null)
{
old.CollectionChanged -= loggerControl.OnWorkCollectionChanged;
}
var nouvelleValeur = e.NewValue as ObservableCollection<LogInfo>;
if (nouvelleValeur != null)
{
nouvelleValeur.CollectionChanged += loggerControl.OnWorkCollectionChanged;
}
// Reset de la liste.
if(old.Count > 0 && nouvelleValeur.Count == 0)
loggerControl.FlowDoc.Blocks.Clear();
}
}
/// <summary>
/// Méthode quand la collection est modifiée.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnWorkCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Reset)
{
// Clear and update entire collection
this.FlowDoc.Blocks.Clear();
}
if (e.Action == NotifyCollectionChangedAction.Add)
{
var nouvelleCollection = sender as ObservableCollection<LogInfo>;
var derniereLigne = nouvelleCollection.Last();
Paragraph paragraphLog = new Paragraph(new Run(derniereLigne.InformationMessage));
paragraphLog.Margin = new Thickness(0, 0, 0, 0);
witch (derniereLigne.StatusLog)
case StatusLog.Error:
paragraphLog.Foreground = Brushes.Red;
break;
case StatusLog.Warn:
paragraphLog.Foreground = Brushes.Orange;
break;
case StatusLog.InfoGreen:
paragraphLog.Foreground = Brushes.Green;
break;
case StatusLog.Information:
default:
paragraphLog.Foreground = Brushes.Black;
break;
this.FlowDoc.Blocks.Add(paragraphLog);
this.RichText.ScrollToEnd();
}
}
}
我正在做一个用户控件,以便能够显示日志。我希望用户能够对 select 行进行复制和粘贴。我计划使用 RichTextBox,因为根据日志行的类型(警告、信息、错误),我会更改颜色。 这是代码:
<UserControl x:Class="WpfAppFrameLogging.UserControls.LoggerControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfAppFrameLogging.UserControls"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<RichTextBox x:Name="RichText"
IsReadOnly="True"
ScrollViewer.VerticalScrollBarVisibility="Auto">
<FlowDocument x:Name="FlowDoc">
<FlowDocument.Resources>
<Style TargetType="{x:Type Paragraph}">
<Setter Property="Margin" Value="0"/>
</Style>
</FlowDocument.Resources>
</FlowDocument>
</RichTextBox>
</Grid>
</UserControl>
在那里,我想要一个 ObservableCollection 类型的依赖属性,当我的控制器中添加了一个日志行时,它会在 RichTextBox 中添加一个新段落。 看起来像这样的东西。
代码隐藏用户控件
var paragraphLog = new Paragraph(new Run(textLog));
paragraphLog.Margin = new Thickness(0, 0, 0, 0);
switch (status)
{
case StatusLog.Error:
paragraphLog.Foreground = Brushes.Red;
break;
case StatusLog.Success:
paragraphLog.Foreground = Brushes.Green;
break;
case StatusLog.Warn:
paragraphLog.Foreground = Brushes.Orange;
break;
case StatusLog.Info: // Useless--> I know
default:
paragraphLog.Foreground = Brushes.Black;
break;
}
FlowDoc.Blocks.Add(paragraphLog);
RichText.ScrollToEnd();
Class 日志信息
public class LogInfo
{
public string TextLog { get; set; }
public StatusLog StatusLog { get; set; }
}
public enum StatusLog
{
Info, // --> Black
Warn, // --> Orange
Error, // --> Red
Success // --> Green
}
在视图中,我将使用 UserControl:
<myControls:LoggerControl AllLogsInfo="{Binding MainLogInfos, Mode=OneWay}" />
和MainLogInfos
public ObservableCollection<LogInfo> MainLogInfos
{
get { return _mainLogInfos; }
set
{
if (value != _mainLogInfos)
{
_mainLogInfos = value;
NotifyPropertyChanged();
}
}
}
private ObservableCollection<LogInfo> _mainLogInfos;
// Method :
public void LogError(string message)
{
MainLogInfos.Add(new LogInfo() { TextLog = message, StatusLog = StatusLog.Error });
}
我仅限于 4.7.2 框架。 感谢您的帮助、想法、建议...
此示例使用带有文本框的列表框。它被设置为看起来像一个 TextBlock。 Style 用于根据 StatusLog 设置颜色。我包含了 4 条不同状态的日志消息。您可以 select 多行,我已经包含了一个用于复制 selected 文本的上下文菜单。
MainWindow.xaml
<Window x:Class="SO65185215.MainWindow"
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:SO65185215"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<ListBox ItemsSource="{Binding MainLogInfos}">
<ListBox.Resources>
<Style x:Key="TextBoxStyle" TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding StatusLog}" Value="Warn">
<Setter Property="Foreground" Value="Orange"/>
</DataTrigger>
<DataTrigger Binding="{Binding StatusLog}" Value="Error">
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding StatusLog}" Value="Success">
<Setter Property="Foreground" Value="Green"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox x:Name="tb" Text="{Binding TextLog}"
BorderThickness="0"
Background="Transparent"
IsReadOnly="True"
Style="{StaticResource TextBoxStyle}">
<TextBox.ContextMenu>
<ContextMenu>
<MenuItem Command="ApplicationCommands.Copy"/>
</ContextMenu>
</TextBox.ContextMenu>
</TextBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
MainWindow.xaml.cs
using System.Collections.ObjectModel;
using System.Windows;
namespace SO65185215
{
public partial class MainWindow : Window
{
public MainWindow()
{
DataContext = this;
InitializeComponent();
Loaded += MainWindow_Loaded;
}
public ObservableCollection<LogInfo> MainLogInfos { get; } = new ObservableCollection<LogInfo>();
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
MainLogInfos.Add(new LogInfo() { TextLog = "log message 1", StatusLog = StatusLog.Info });
MainLogInfos.Add(new LogInfo() { TextLog = "log message\nNext line message 2", StatusLog = StatusLog.Error });
MainLogInfos.Add(new LogInfo() { TextLog = "log message3\nWarning", StatusLog = StatusLog.Warn });
MainLogInfos.Add(new LogInfo() { TextLog = "log message success", StatusLog = StatusLog.Success });
}
}
public class LogInfo
{
public string TextLog { get; set; }
public StatusLog StatusLog { get; set; }
}
public enum StatusLog
{
Info, // --> Black
Warn, // --> Orange
Error, // --> Red
Success // --> Green
}
}
编辑:
如果您的目的是 select 多条消息,那么您可以将 ListBox 设置为 Multipel/Extended selectionMode:
<ListBox x:Name="listBox" ItemsSource="{Binding MainLogInfos}" SelectionMode="Multiple">
将上下文菜单移至列表框
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Copy selected" Click="CopySelected_Clicked"/>
</ContextMenu>
</ListBox.ContextMenu>
在 Click 事件处理程序中将 selected 文本连接到剪贴板。 观察 selectedItems 集合包含消息的顺序是 selected。
private void CopySelected_Clicked(object sender, RoutedEventArgs e)
{
StringBuilder sb = new StringBuilder();
foreach(LogInfo item in listBox.SelectedItems)
{
sb.AppendLine(item.TextLog);
}
if (sb.Length > 0)
Clipboard.SetText(sb.ToString());
}
我找到了解决方案
我的LoggerControl.xaml
<UserControl x:Class="SDK.FrontOffice.CustomControl.LoggerControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:SDK.FrontOffice.CustomControl"
xmlns:controls="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
xmlns:converter="clr-namespace:SDK.FrontOffice.Converters"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<!--<Grid.Resources>
<converter:TypeInfoToColorConverter x:Key="converterTypeInfo" />
</Grid.Resources>-->
<!--<GroupBox Header="Logs">-->
<RichTextBox x:Name="RichText"
IsReadOnly="True"
ScrollViewer.VerticalScrollBarVisibility="Auto">
<FlowDocument x:Name="FlowDoc">
<FlowDocument.Resources>
<Style TargetType="{x:Type Paragraph}">
<Setter Property="Margin" Value="0"/>
</Style>
</FlowDocument.Resources>
</FlowDocument>
</RichTextBox>
<!--</GroupBox>-->
</Grid>
</UserControl>
和代码隐藏
/// <summary>
/// Logique d'interaction pour LoggerControl.xaml
/// </summary>
public partial class LoggerControl : UserControl
{
public LoggerControl()
{
InitializeComponent();
}
#region DependencyProperty
public ObservableCollection<LogInfo> AllLogs
{
get { return (ObservableCollection<LogInfo>)GetValue(AllLogsProperty); }
set { SetValue(AllLogsProperty, value); }
}
// Using a DependencyProperty as the backing store for AllLogs. This enables animation, styling, binding, etc...
public static readonly DependencyProperty AllLogsProperty =
DependencyProperty.Register("AllLogs",
typeof(ObservableCollection<LogInfo>),
typeof(LoggerControl),
new PropertyMetadata(new ObservableCollection<LogInfo>(),
OnAllLogsChanged));
private static void OnAllLogsChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
LoggerControl loggerControl = sender as LoggerControl;
var old = e.OldValue as ObservableCollection<LogInfo>;
if (old != null)
{
old.CollectionChanged -= loggerControl.OnWorkCollectionChanged;
}
var nouvelleValeur = e.NewValue as ObservableCollection<LogInfo>;
if (nouvelleValeur != null)
{
nouvelleValeur.CollectionChanged += loggerControl.OnWorkCollectionChanged;
}
// Reset de la liste.
if(old.Count > 0 && nouvelleValeur.Count == 0)
loggerControl.FlowDoc.Blocks.Clear();
}
}
/// <summary>
/// Méthode quand la collection est modifiée.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnWorkCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Reset)
{
// Clear and update entire collection
this.FlowDoc.Blocks.Clear();
}
if (e.Action == NotifyCollectionChangedAction.Add)
{
var nouvelleCollection = sender as ObservableCollection<LogInfo>;
var derniereLigne = nouvelleCollection.Last();
Paragraph paragraphLog = new Paragraph(new Run(derniereLigne.InformationMessage));
paragraphLog.Margin = new Thickness(0, 0, 0, 0);
witch (derniereLigne.StatusLog)
case StatusLog.Error:
paragraphLog.Foreground = Brushes.Red;
break;
case StatusLog.Warn:
paragraphLog.Foreground = Brushes.Orange;
break;
case StatusLog.InfoGreen:
paragraphLog.Foreground = Brushes.Green;
break;
case StatusLog.Information:
default:
paragraphLog.Foreground = Brushes.Black;
break;
this.FlowDoc.Blocks.Add(paragraphLog);
this.RichText.ScrollToEnd();
}
}
}