自定义控件绑定不会更新模型

问题描述 投票:-1回答:2

我使用自定义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事件被触发时,HistoryRedoStack在模型上没有更新,而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();
}
c# wpf data-binding dependency-properties
2个回答
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));}
}

因此,我在SetValueSetCurrentValue上使用History而不是RedoStack,以使UI正确更新模型。


0
投票

使用INotifyPropertyChanged接口并实现OnPropertyChanged方法并将该方法放在需要更新接口的位置

© www.soinside.com 2019 - 2024. All rights reserved.