[英]TextBox Binding doesn't work when switching Tab in 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.