我正在使用自定义WindowsFormsHost
控件将FastColoredTextbox winforms控件包装在一个选项卡控件中,当用户通过DataTemplate
点击新文档按钮时,该控件的动态创建选项卡。
由于此SO question中解释的选项卡控件的限制,我的撤消/重做逻辑保留了每个选项卡的所有选项卡的历史记录,这导致显示错误的文本。
所以我决定修改FastColoredTextbox控件以公开历史记录和重做堆栈,并将它们添加到我的自定义WindowsFormsHost包装器控件作为依赖项属性,这两种方式绑定到我的DocumentModel。
XAML:
<TabControl TabStripPlacement="Top" Margin="5" ItemsSource="{Binding Documents}" SelectedItem="{Binding CurrentDocument, Mode=TwoWay}" x:Name="TabDocuments">
<TabControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="10" MaxWidth="10" MinWidth="10"/>
<ColumnDefinition Width="10" MaxWidth="10" MinWidth="10"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Metadata.FileName}"/>
<TextBlock Grid.Column="1" Text="*" Visibility="{Binding IsSaved, Converter={StaticResource VisibilityConverter}}"/>
<Button Grid.Column="2" Width="10" Height="10" MaxWidth="10" MaxHeight="10" MinWidth="10" MinHeight="10" VerticalAlignment="Center" HorizontalAlignment="Right" Command="{Binding CloseDocumentButtonCommand}">
<TextBlock Text="X" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="8" FontWeight="Bold"/>
</Button>
</Grid>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate x:Name="TabDocumentsDataTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border BorderBrush="Black" BorderThickness="1" Grid.Column="0" Margin="5">
<customControls:CodeTextboxHost Text="{Binding Markdown, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" History="{Binding History, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" RedoStack="{Binding RedoStack, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" WordWrap="True" x:Name="CodeTextboxHost"/>
</Border>
<Border BorderBrush="Black" BorderThickness="1" Grid.Column="1" Margin="5">
<wpf:ChromiumWebBrowser Address="{Binding Html, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" x:Name="ChromiumWebBrowser"/>
</Border>
</Grid>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
模型的相关部分:
public ObservableCollection<UndoableCommand> History
{
get { return _history; }
set
{
_history = value;
OnPropertyChanged();
}
}
public ObservableCollection<UndoableCommand> RedoStack
{
get { return _redoStack; }
set
{
_redoStack = value;
OnPropertyChanged();
}
}
自定义控制代码的相关部分:
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(CodeTextboxHost), new PropertyMetadata("", new PropertyChangedCallback(
(d, e) =>
{
var textBoxHost = d as CodeTextboxHost;
if (textBoxHost != null && textBoxHost._innerTextbox != null)
{
textBoxHost._innerTextbox.Text = textBoxHost.GetValue(e.Property) as string;
}
}), null));
public static readonly DependencyProperty HistoryProperty = DependencyProperty.Register("History", typeof(LimitedStack<UndoableCommand>), typeof(CodeTextboxHost), new PropertyMetadata(new LimitedStack<UndoableCommand>(200), new PropertyChangedCallback(
(d, e) =>
{
var textBoxHost = d as CodeTextboxHost;
if (textBoxHost != null && textBoxHost._innerTextbox != null)
{
var history = textBoxHost.GetValue(e.Property) as LimitedStack<UndoableCommand>;
if (history != null)
{
textBoxHost._innerTextbox.TextSource.Manager.History = history;
textBoxHost._innerTextbox.OnUndoRedoStateChanged();
}
else
{
textBoxHost._innerTextbox.ClearUndo();
}
}
}), null));
public static readonly DependencyProperty RedoStackProperty = DependencyProperty.Register("RedoStack", typeof(Stack<UndoableCommand>), typeof(CodeTextboxHost), new PropertyMetadata(new Stack<UndoableCommand>(), new PropertyChangedCallback(
(d, e) =>
{
var textBoxHost = d as CodeTextboxHost;
if (textBoxHost != null && textBoxHost._innerTextbox != null)
{
var redoStack = textBoxHost.GetValue(e.Property) as Stack<UndoableCommand>;
if (redoStack != null)
{
textBoxHost._innerTextbox.TextSource.Manager.RedoStack = redoStack;
textBoxHost._innerTextbox.OnUndoRedoStateChanged();
}
else
{
textBoxHost._innerTextbox.ClearUndo();
}
}
}), null));
public LimitedStack<UndoableCommand> History
{
get { return (LimitedStack<UndoableCommand>) GetValue(HistoryProperty);}
set { SetValue(HistoryProperty, value);}
}
public Stack<UndoableCommand> RedoStack
{
get { return (Stack<UndoableCommand>) GetValue(RedoStackProperty); }
set { SetValue(RedoStackProperty, value);}
}
public CodeTextboxHost()
{
Child = _innerTextbox;
_innerTextbox.Language = FastColoredTextBoxNS.Language.Custom;
_innerTextbox.DescriptionFile = AppDomain.CurrentDomain.BaseDirectory + "SyntaxConfig\\MarkdownSyntaxHighlighting.xml";
_innerTextbox.HighlightingRangeType = HighlightingRangeType.AllTextRange;
_innerTextbox.TextChanged += _innerTextbox_TextChanged;
}
private void _innerTextbox_TextChanged(object sender, TextChangedEventArgs e)
{
Text = _innerTextbox.Text;
History = _innerTextbox.TextSource.Manager.History;
RedoStack = _innerTextbox.TextSource.Manager.RedoStack;
}
我的问题是,当TextChanged
事件被触发时,History
或RedoStack
在Text
完全更新时未在模型上更新。
我尝试添加RelativeSource,因为SO question建议但是它没有用。
任何帮助/想法将不胜感激。谢谢。
编辑1:
如我所知,我制作了所有收藏品ObservableCollection,但它没有做出任何改变,因为模型再次没有更新。
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(CodeTextboxHost), new PropertyMetadata("", new PropertyChangedCallback(
(d, e) =>
{
var textBoxHost = d as CodeTextboxHost;
if (textBoxHost != null && textBoxHost._innerTextbox != null)
{
textBoxHost._innerTextbox.Text = textBoxHost.GetValue(e.Property) as string;
}
}), null));
public static readonly DependencyProperty HistoryProperty = DependencyProperty.Register("History", typeof(ObservableCollection<UndoableCommand>), typeof(CodeTextboxHost), new PropertyMetadata(new ObservableCollection<UndoableCommand>(), new PropertyChangedCallback(
(d, e) =>
{
var textBoxHost = d as CodeTextboxHost;
if (textBoxHost != null && textBoxHost._innerTextbox != null)
{
var history = textBoxHost.GetValue(e.Property) as ObservableCollection<UndoableCommand>;
if (history != null)
{
textBoxHost._innerTextbox.TextSource.Manager.History = history.ToLimitedStack(200);
textBoxHost._innerTextbox.OnUndoRedoStateChanged();
}
else
{
textBoxHost._innerTextbox.ClearUndo();
}
}
}), null));
public static readonly DependencyProperty RedoStackProperty = DependencyProperty.Register("RedoStack", typeof(ObservableCollection<UndoableCommand>), typeof(CodeTextboxHost), new PropertyMetadata(new ObservableCollection<UndoableCommand>(), new PropertyChangedCallback(
(d, e) =>
{
var textBoxHost = d as CodeTextboxHost;
if (textBoxHost != null && textBoxHost._innerTextbox != null)
{
var redoStack = textBoxHost.GetValue(e.Property) as ObservableCollection<UndoableCommand>;
if (redoStack != null)
{
textBoxHost._innerTextbox.TextSource.Manager.RedoStack = redoStack.ToStack();
textBoxHost._innerTextbox.OnUndoRedoStateChanged();
}
else
{
textBoxHost._innerTextbox.ClearUndo();
}
}
}), null));
public ObservableCollection<UndoableCommand> History
{
get { return (ObservableCollection<UndoableCommand>) GetValue(HistoryProperty);}
set { SetValue(HistoryProperty, value);}
}
public ObservableCollection<UndoableCommand> RedoStack
{
get { return (ObservableCollection<UndoableCommand>) GetValue(RedoStackProperty); }
set { SetValue(RedoStackProperty, value);}
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public bool WordWrap
{
get { return (bool)GetValue(WordWrapProperty); }
set { SetValue(WordWrapProperty, value); }
}
public CodeTextboxHost()
{
Child = _innerTextbox;
_innerTextbox.Language = FastColoredTextBoxNS.Language.Custom;
_innerTextbox.DescriptionFile = AppDomain.CurrentDomain.BaseDirectory + "SyntaxConfig\\MarkdownSyntaxHighlighting.xml";
_innerTextbox.HighlightingRangeType = HighlightingRangeType.AllTextRange;
_innerTextbox.TextChanged += _innerTextbox_TextChanged;
}
private void _innerTextbox_TextChanged(object sender, TextChangedEventArgs e)
{
Text = _innerTextbox.Text;
History = _innerTextbox.TextSource.Manager.History.ToOveObservableCollection();
RedoStack = _innerTextbox.TextSource.Manager.RedoStack.ToObservableCollection();
}
答案 0 :(得分:0)
我能够通过修改自定义控制代码使UI更改模型,如下所示:
public static readonly DependencyProperty HistoryProperty = DependencyProperty.Register("History", typeof(IEnumerable<UndoableCommand>), typeof(CodeTextboxHost), new FrameworkPropertyMetadata(new ObservableCollection<UndoableCommand>(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(
(d, e) =>
{
var textBoxHost = d as CodeTextboxHost;
if (textBoxHost != null && textBoxHost._innerTextbox != null)
{
var history = textBoxHost.GetValue(e.Property) as ObservableCollection<UndoableCommand>;
if (history != null)
{
textBoxHost._innerTextbox.TextSource.Manager.History = history.ToLimitedStack(200);
textBoxHost._innerTextbox.OnUndoRedoStateChanged();
}
else
{
textBoxHost._innerTextbox.ClearUndo();
}
}
}), null));
public static readonly DependencyProperty RedoStackProperty = DependencyProperty.Register("RedoStack", typeof(IEnumerable<UndoableCommand>), typeof(CodeTextboxHost), new FrameworkPropertyMetadata(new ObservableCollection<UndoableCommand>(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(
(d, e) =>
{
var textBoxHost = d as CodeTextboxHost;
if (textBoxHost != null && textBoxHost._innerTextbox != null)
{
var redoStack = textBoxHost.GetValue(e.Property) as ObservableCollection<UndoableCommand>;
if (redoStack != null)
{
textBoxHost._innerTextbox.TextSource.Manager.RedoStack = redoStack.ToStack();
textBoxHost._innerTextbox.OnUndoRedoStateChanged();
}
else
{
textBoxHost._innerTextbox.ClearUndo();
}
}
}), null));
public ObservableCollection<UndoableCommand> History
{
get { return (ObservableCollection<UndoableCommand>) GetValue(HistoryProperty);}
set { SetCurrentValue(HistoryProperty, new ObservableCollection<UndoableCommand>(value));}
}
public ObservableCollection<UndoableCommand> RedoStack
{
get { return (ObservableCollection<UndoableCommand>) GetValue(RedoStackProperty); }
set { SetCurrentValue(RedoStackProperty, new ObservableCollection<UndoableCommand>(value));}
}
因此我在SetValue
和SetCurrentValue
上使用History
代替RedoStack
,以使UI正确更新模型。
答案 1 :(得分:0)
使用INotifyPropertyChanged
接口并实现OnPropertyChanged
方法并将该方法放在要更新接口的位置