我的View中有一个TabControl,我动态添加包含文本框作为内容的TabItems。当想要从Selected Item中获取Line Count时,它总是返回-1,同时使用textbox.GetLastVisibleLineIndex()。代码如下:
我的观点:
<TabControl x:Name="tabControl" HorizontalAlignment="Left" Height="385" Margin="5,50,0,0" VerticalAlignment="Top" Width="740" ItemsSource="{Binding Tabs, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedItem="{ Binding SelectedTab, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
<TabControl.ItemTemplate>
<!-- this is the header template-->
<DataTemplate>
<TextBlock
Text="{Binding Header}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<!-- this is the body of the TabItem template-->
<DataTemplate>
<TextBox
Text="{Binding Text, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged,diag:PresentationTraceSources.TraceLevel=High }" AcceptsReturn="True" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyUp">
<cmd:EventToCommand Command="{Binding DataContext.TextChanged, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
我的ViewModel:
TabItem tabItem = new TabItem();
tabItem.Header = mainModel.Header;
TextBox textBox = new TextBox();
textBox.Text = mainModel.TextFile;
tabItem.LayoutUpdated += (sender2, e2) => textBox_LayoutUpdated(sender2, e2);
textBox.LayoutUpdated += (sender3, e3) => textBox_LayoutUpdated(sender3, e3);
tabItem.Content = textBox;
Tabs.Add(tabItem);
SelectedTab = tabItem;
private void textBox_LayoutUpdated(object sender, EventArgs args)
{
lineCount = ((SelectedTab as TabItem).Content as TextBox).LineCount;
}
MainModel是MVVM中的模型。
我的View.cs:
this.UpdateLayout();
TabItem tab = this.tabControl.SelectedItem as TabItem;
int index = ((this.tabControl.SelectedItem as TabItem).Content as TextBox).GetLastVisibleLineIndex();
即使在View.cs中,总是-1;
我是WPF MVVM的新手,
感谢。
答案 0 :(得分:1)
您当前的代码存在许多问题。看来你实际上错过了MVVM的观点。
首先,视图模型不能知道视图,也不能知道任何特定于视图的类型(例如TabItem
)。视图模型只是一个采用模型的层,可以通过视图呈现此模型。视图模型不能像您在示例中那样构造视图本身。
您获得-1的原因是您添加到标签项中的TextBox
将永远不会被布局,因为您覆盖标签项的ContentTemplate
。
还有其他一些事情你做错了或者没有必要:
Binding
的ItemsSource
不能是TwoWay
,因为标签控件本身永远不会更新此属性值。出于同样的原因,此处不需要UpdateSourceTrigger
。UpdateSourceTrigger
绑定不需要SelectedItem
,因为它默认为PropertyChanged
模式TextChanged
的命令,但按照惯例,它必须命名为TextChangedCommand
(带有Command
后缀)tabItem.LayoutUpdated += (sender2, e2) => textBox_LayoutUpdated(sender2, e2)
对于创建不必要的lambda捕获this
的事件订阅来说是一种过度杀手,而是使用方法组语法:tabItem.LayoutUpdated += textBox_LayoutUpdated
假设您有项目的视图模型:
class Item : ViewModelBase
{
public Item(string header, string textFile)
{
Header = header;
this.textFile = textFile;
}
public string Header { get; }
private string textFile;
public string TextFile
{
get => textFile;
set { textFile = value; OnPropertyChanged(); }
}
private int lineCount;
public int LineCount
{
get => lineCount;
set { lineCount = value; OnPropertyChanged(); Debug.WriteLine("Line count is now: " + value); }
}
}
此视图模型表示将显示为选项卡项的单个项目。但也许这将是未来的一些其他控制 - 实际上你不必为此烦恼。视图模型不知道哪个控件完全将显示值。视图模型只是以方便的方式提供这些值。
因此,Header
和TextFile
属性包含模型值。 LineCount
属性将由视图计算(详见下文)。
主视图模型如下所示:
class ViewModel : ViewModelBase
{
public ObservableCollection<Item> Items { get; } = new ObservableCollection<Item>();
private Item selectedItem;
public Item SelectedItem
{
get => selectedItem;
set { selectedItem = value; OnPropertyChanged(); }
}
}
请注意,Items
集合属性是只读的。这意味着没有人可以更改对集合的引用,但集合本身不是只读的。但是,SelectedItem
引用可能会更新。
现在重点:LineCount
的{{1}}属性也会更新,例如当TextBox
包装文本并调整控件的大小时。所以我们不能只计算视图模型中的行数,我们需要在视图中执行此操作。
但是,正如我们所知,视图模型必须不了解视图。该怎么办?在这种情况下,优秀的开发人员更喜欢TextBox
命名空间中的Behavior
。
让我们创建一个简单的行为来监控System.Windows.Interactivity
的{{1}}:
LineCount
现在我们可以将此行为附加到任何TextBox
并使用行为的class LineCountBehavior : Behavior<TextBox>
{
public int LineCount
{
get { return (int)GetValue(LineCountProperty); }
set { SetValue(LineCountProperty, value); }
}
public static readonly DependencyProperty LineCountProperty =
DependencyProperty.Register("LineCount", typeof(int), typeof(LineCountBehavior), new PropertyMetadata(0));
protected override void OnAttached()
{
AssociatedObject.LayoutUpdated += RefreshLineCount;
AssociatedObject.TextChanged += RefreshLineCount;
}
protected override void OnDetaching()
{
AssociatedObject.LayoutUpdated -= RefreshLineCount;
AssociatedObject.TextChanged -= RefreshLineCount;
}
private void RefreshLineCount(object sender, EventArgs e)
{
LineCount = AssociatedObject.LineCount;
}
}
依赖项属性作为绑定源。这是一个完整的XAML设置:
TextBox
所以这是一个'干净'的MVVM解决方案。希望我能给你一些见解。
顺便说一句,我不知道为什么你需要在视图模型中使用这个行数...