簡體   English   中英

WPF TabControl 綁定不允許我更新選項卡數據

[英]WPF TabControl binding doesn't let me update tab data

我有一項大學作業,要使用 WPF 創建一個類似於 Notepad++ 的應用程序。為此,我在 MainWindow.xaml 中創建了一個 TabControl,每個選項卡都有一個 header (TextBlock) 和文本內容 (TextBox)。 TabControl 綁定到名為“Tabs”的 TabModel 的 ObservableCollection(我用於選項卡項的 model class)。

調用“另存為”function 時出現問題。 它應該將選項卡內容保存在 a.txt 文件中(這工作得很好)並重命名活動選項卡 header 以匹配已保存的.txt 文件的名稱(它不會更新標題)。 調試顯示“選項卡”內的數據已正確更新,但 UI 並未反映出來。

這是 MainWindow.xaml 中的代碼:

<Window x:Class="Notepad__.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:Notepad__.Commands"
        mc:Ignorable="d"
        Title="Notepad--"
        Name="WindowHeader"
        Height="1000"
        Width="1200"
        WindowStartupLocation="CenterScreen"
        Icon="Images\icon.png">
    <Window.DataContext>
        <local:FileCommands/>
    </Window.DataContext>
    <Grid>
        <Grid.Background>
            <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                <GradientStop Color="#E7E9BB" Offset="0.0" />
                <GradientStop Color="#403B4A" Offset="1.0" />
            </LinearGradientBrush>
        </Grid.Background>
        <Grid.RowDefinitions>
            <RowDefinition Height="25"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <Menu>
            <Menu.ItemsPanel>
                <ItemsPanelTemplate>
                    <DockPanel HorizontalAlignment="Left"/>
                </ItemsPanelTemplate>
            </Menu.ItemsPanel>
            <MenuItem Header="File" FontSize="14">
                <MenuItem Header="New" Command="{Binding Path=CommandNew}"/>
                <MenuItem Header="Open..." Command="{Binding Path=CommandOpen}"/>
                <MenuItem Header="Save"/>
                <MenuItem Header="Save as..." Command="{Binding Path=CommandSaveAs}"/>
                <Separator/>
                <MenuItem Header="Exit" Command="{Binding Path=CommandExit}"/>
            </MenuItem>
            <MenuItem Header="Search" FontSize="14">
                <MenuItem Header="Find..."/>
                <MenuItem Header="Replace..."/>
                <MenuItem Header="Replace All..."/>
            </MenuItem>
            <MenuItem Header="Help" FontSize="14">
                <MenuItem Header="About"/>
            </MenuItem>
        </Menu>

        <TabControl
            Name = "TabsControl"
            ItemsSource="{Binding Tabs, UpdateSourceTrigger=PropertyChanged}"
            SelectedIndex="{Binding CurrentTab, UpdateSourceTrigger=PropertyChanged}"
            Grid.Row="1"
            Margin="25"
            FontSize="14">
            <TabControl.ItemTemplate>
                <DataTemplate>
                    <TextBlock
                    Text="{Binding Filename, UpdateSourceTrigger=PropertyChanged}" />
                </DataTemplate>
            </TabControl.ItemTemplate>
            <TabControl.ContentTemplate>
                <DataTemplate>
                    <TextBox
                    Text="{Binding Content, UpdateSourceTrigger=PropertyChanged}"
                    TextWrapping="Wrap"
                    AcceptsReturn="True"
                    VerticalScrollBarVisibility="Visible"
                    HorizontalAlignment="Stretch"
                    VerticalAlignment="Stretch"
                    Grid.Row="1"
                    Margin="10"
                    FontSize="26"/>
                </DataTemplate>
            </TabControl.ContentTemplate>
        </TabControl>

    </Grid>
</Window>

這是 FileCommands.cs 中的代碼,我在其中執行綁定和 SaveAs function 的實現。我排除了“使用”:

namespace Notepad__.Commands
{
    class FileCommands : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public ObservableCollection<TabModel> Tabs { get; set; }

        private int currentTab;
        public int CurrentTab { get => currentTab; set => SetProperty(ref currentTab, value); }

        private ICommand m_new;
        private ICommand m_open;
        private ICommand m_saveAs;

        public FileCommands()
        {
            Tabs = new ObservableCollection<TabModel>();
            Tabs.Add(new TabModel { Filename = "File 1", Content = "" });
        }

        private void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        public void New(object parameter)
        {
            Tabs.Add(new TabModel { Filename = "File " + (Tabs.Count + 1), Content = "" });
        }

        public void Open(object parameter)
        {
            var openFileDialog = new OpenFileDialog
            {
                Title = "Select a text file...",
                Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*"
            };

            if (openFileDialog.ShowDialog() == true)
            {
                Stream fileStream = openFileDialog.OpenFile();

                using (StreamReader reader = new StreamReader(fileStream))
                {
                    Tabs.Add(new TabModel { Filename = openFileDialog.SafeFileName, Content = reader.ReadToEnd() });
                    OnPropertyChanged("Tabs");
                }
            }
        }

        public void SaveAs(object parameter)
        {
            var saveFileDialog = new SaveFileDialog
            {
                Title = "Save...",
                Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*"
            };

            if (saveFileDialog.ShowDialog() == true)
            {
                File.WriteAllText(saveFileDialog.FileName, Tabs[currentTab].Content);
                Tabs[currentTab].Filename = saveFileDialog.SafeFileName;
            }
            OnPropertyChanged("Tabs");
        }

        public ICommand CommandNew
        {
            get
            {
                if (m_new == null)
                    m_new = new RelayCommand(New);
                return m_new;
            }
        }

        public ICommand CommandOpen
        {
            get
            {
                if (m_open == null)
                    m_open = new RelayCommand(Open);
                return m_open;
            }
        }

        public ICommand CommandSaveAs
        {
            get
            {
                if (m_saveAs == null)
                    m_saveAs = new RelayCommand(SaveAs);
                return m_saveAs;
            }
        }
    
        protected bool SetProperty<T>(ref T field, T newValue, [CallerMemberName] string propertyName = null)
        {
            if (!Equals(field, newValue))
            {
                field = newValue;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
                return true;
            }

            return false;
        }

    }
}

我還包含了函數“New”和“Open”的代碼。 這是因為它們工作正常,使用 Tabs.Add 添加新選項卡會按預期工作,並且 UI 會相應更新。 這就是讓我感到困惑的地方:為什么添加選項卡有效而更新選項卡無效?

對不起,如果這個問題是微不足道的。 這是我第一次在C#工作,也是我的第一個WPF項目。 我的同學和實驗室老師都無法幫助我解決問題。 感謝閱讀,如果我的代碼中還有其他我應該包含的內容,請告訴我:)

正如評論中所討論的, TabModel需要實現INotifyPropertyChanged接口,以便在每次更改屬性值時更新 UI。

您可以創建一個 base class 來引發通知。

請參見下面的示例:

基地:

internal abstract class ObservableBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected bool SetProperty<T>(ref T field, T newValue, [CallerMemberName] string propertyName = null)
    {
        if (Equals(field, newValue))
            return false;

        field = newValue;
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        return true;

    }
}

model,從基派生:

internal class TabModel : ObservableBase
{
    private string _name;
    private string _content;

    public string Filename
    {
        get => _name;
        set => SetProperty(ref _name, value);
    }

    public string Content
    {
        get => _content;
        set => SetProperty(ref _content, value);
    }
}

虛擬機也一樣:

class FileCommands : ObservableBase
{
    // ...
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM