将ItemsSource设置为列表时,使“ IsMouseOver”在ListView上工作

时间:2019-06-27 12:37:13

标签: c# wpf events

我有一个WPF项目,我在其中创建了一个UserControl,目的是制作一个自定义ListViewItem,其中包括一个关闭按钮。这是该代码:

<ListViewItem x:Name="lviTab" Height="36" Background="#232323" 
MouseUp="LviTab_MouseUp" MouseEnter="LviTab_MouseEnter">
    <StackPanel Orientation="Horizontal">
        <TextBlock Text="{Binding TabText}" FontSize="15" />
        <ListViewItem x:Name="lviTabClose" Margin="5, 0, 0, 0" 
Padding="0, 0, 0, 0" MouseUp="LviTabClose_MouseUp">
            <materialDesign:PackIcon Kind="Close" Foreground="White" 
VerticalAlignment="Center"

HorizontalAlignment="Center" Width="20" Height="20" />
        </ListViewItem>
    </StackPanel>
</ListViewItem>

我通过将UserControl内部的TextBlock文本绑定到另一个类TabData.cs来调整每个项目内部的文本

public class TabData : UIElement
{
    public TabData() : base()
    {
    }

    public string TabText { get; set; }
}

在ListView中,我已将DataTemplate设置为UserControl。我已经将ListView ItemSource设置为TabData对象的列表。

tabs = new List<TabData>()
        {
            new TabData{TabText = "Tab 1"},
            new TabData{TabText = "Tab 2"},
            new TabData{TabText = "Tab 3"},
            new TabData{TabText = "Tab 4"},
            new TabData{TabText = "Tab 5"}
        };

        lvTabs.ItemsSource = tabs;

将鼠标移到ListViewItem上时,我需要IsMouseOver为true。我尝试将UIElement继承到TabData,但没有成功。我对WPF还是很陌生,希望对使用UserControl并将ItemSource设置为没有ListViewItems的项目时如何保留IsMouseOver属性提供帮助。

1 个答案:

答案 0 :(得分:1)

这是做您想做的最简单的方法。当用集合中的项目填充ListView时,它会创建自己的ListViewItems。您无需在每个ListViewItems内创建另一个ListViewItem,而不必在您自己的列表内创建第三个ListViewItem。

我将省去UserControl并将该XAML直接放在模板中。这样做的原因是我们希望单击处理程序位于MainWindow.xaml.cs中,并可以在其中与主视图模型进行交互。我们可以通过两种不同的方式很容易地使它与UserControl一起使用,但我会尽量简化这一过程。理想情况下,您应该使用Command,但这是一种特殊情况,其中MVVM中的少量杂质不会破坏您。对于项UI像一个文本块和一个按钮一样简单的情况,UserControl超出了您的需要。

首先是MainWindow.xaml

<Window.Resources>
    <DataTemplate x:Key="TabListViewItemTemplate">
        <DockPanel LastChildFill="False">
            <TextBlock Text="{Binding TabText}" DockPanel.Dock="Left" />
            <Button x:Name="TabCloseButton" Click="TabCloseButton_Click" DockPanel.Dock="Right" >
                <Button.Template>
                    <ControlTemplate TargetType="Button">
                        <materialDesign:PackIcon 
                            Kind="Close" Foreground="White" 
                            VerticalAlignment="Center" HorizontalAlignment="Center" 
                            Width="20" Height="20" />
                    </ControlTemplate>
                </Button.Template>
            </Button>
        </DockPanel>
    </DataTemplate>
</Window.Resources>
<Grid>
    <ListView
        ItemsSource="{Binding TabItems}"
        ItemTemplate="{StaticResource TabListViewItemTemplate}"
        HorizontalContentAlignment="Stretch"
        />
</Grid>

MainWindow.xaml.cs

public MainWindow()
{
    InitializeComponent();

    DataContext = new MainViewModel
    {
        TabItems = {
            new TabData { TabText = "Fred" },
            new TabData { TabText = "Ginger" },
            new TabData { TabText = "Herman" },
        }
    };
}

private void TabCloseButton_Click(object sender, RoutedEventArgs e)
{
    if ((sender as FrameworkElement).DataContext is TabData tabData)
    {
        MessageBox.Show($"TabCloseButton_Click() {tabData.TabText}");
    }
}

TabData类。不要继承UIElement。它不是用户界面中的元素,只是一个普通的C#类。如果要让用户编辑TabText,则将其设为实现INotifyPropertyChanged的视图模型。

    public class TabData
    {
        public string TabText { get; set; }
    }

主视图模型。实际上,我们在这里所做的任何事情实际上都不需要在您的任何类上使用INotifyPropertyChanged,但是如果您将其转换为有用的东西,则将需要它,因此我们将其包含在内。

public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propName = null) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}

public class MainViewModel : ViewModelBase
{
    public ObservableCollection<TabData> TabItems { get; } = new ObservableCollection<TabData>();
}