自定义控件绑定不会更新模型
我使用自定义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();
}
我能够使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更新模型正确。
使用INotifyPropertyChanged
接口和实现OnPropertyChanged
方法,并将该方法要更新UI
也许一个更好的答案的链接将有所帮助:https://stackoverflow.com/questions/7934236/inotifypropertychanged-and-observablecollection-wpf – Digvijay
我不想更新用户界面,目前的数据绑定工程从模型到UI很好。问题是,当我在可观察集合中进行UI更新时,它不更新模型的可观察集合。 –
在您的xaml模式中使用双向绑定。 –
如果我猜的话,我会说这个问题与您在模型中使用'ObservableCollection'的事实开始的地方做,依赖项属性中的'Stack'和'LimitedStack'。当这些匹配时,我只有绑定集合的好运气。我全部使用'ObservableCollection'。 –
我无法弄清楚完整的代码,但是如果你想明确地调用更新,就有解决方法。尝试交互触发TextBox文本更改它将调用带参数的命令,然后您可以记录您的更改DS – Ramankingdom
@Ramankingdom我实际上使用它,唯一的区别是,文本框本身是一个winforms控件,我用它包装'WindowsFormsHost'。我使用内部文本框的TextChanged事件来更新文本,历史记录和重做堆栈(可以在'_innerTextbox_TextChanged'方法中看到)。但是,问题只是文本更新,其余部分不会更新并存储回模型中。 –