首页 文章

如何动态更改ListBox项的颜色(使用绑定到它的类对象列表)? C#UWP

提问于
浏览
1

我使用两个ListBox元素 . 一个是父(BucketListBox),它决定了应该在子节目中显示的内容 . Child(ObjectListBox)处于多选模式 . 一旦我选择了子元素中的特定元素,我转移到另一个父元素,子元素ListBox将更新为新的选择 .

当用户切换回先前浏览过的父母时,他应该能够看到他当时选择的孩子中的旧选择 . 我正在维护用户在孩子中选择的项目列表 . 我想使用此列表来更改他之前选择的元素的背景颜色 .

截至目前,这些元素仍处于选中状态,但未在UI中显示 .

这是我父母的XAML代码:

<ListBox Grid.Row="1" x:Name="BucketListBox" HorizontalContentAlignment="Stretch" SelectionChanged="BucketListBox_SelectionChanged">
<ListBox.ItemTemplate>
    <DataTemplate>
        <Grid Margin="10,10,10,10">
            <Grid.RowDefinitions>
                <RowDefinition Height="2*"/>
                <RowDefinition Height="1*"/>
            </Grid.RowDefinitions>
            <RelativePanel Grid.Row="0">
                <TextBlock x:Name="BucketNameLabel" x:Uid="NameLabel"/>
                <TextBlock Grid.Row="0" x:Name="BucketNameTextBlock" RelativePanel.AlignBottomWith="BucketNameLabel" RelativePanel.RightOf="BucketNameLabel" Text="{Binding BucketName}" Margin="10,10,10,10"/>
            </RelativePanel>
            <RelativePanel Grid.Row="1">
                <TextBlock x:Name="BucketCreationDateLabel" FontSize="12" x:Uid="CreationDateLabel" HorizontalAlignment="Right"/>
                <TextBlock x:Name="BucketCreationDateTextBlock" FontSize="12" RelativePanel.RightOf="BucketCreationDateLabel" RelativePanel.AlignBottomWith="BucketCreationDateLabel" Text="{Binding BucketCreationDate}" Margin="10,10,10,10" HorizontalAlignment="Right" />
            </RelativePanel>
        </Grid>
    </DataTemplate>
</ListBox.ItemTemplate>

子ListBox的代码:

<ListBox Grid.Row="1" Grid.Column="1" x:Name="ObjectListBox" HorizontalContentAlignment="Stretch" SelectionMode="Multiple">

        <ListBox.ItemTemplate >
            <DataTemplate >
                <Grid Margin="10,10,10,10">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="2*"/>
                        <RowDefinition Height="1*"/>
                        <RowDefinition Height="1*"/>
                    </Grid.RowDefinitions>
                    <RelativePanel Grid.Row="0">
                        <TextBlock x:Name="NameLabel" x:Uid="NameLabel" Margin="10,10,10,10" />
                        <TextBlock x:Name="ObjectNameTextBlock" Text="{Binding ObjectName}" RelativePanel.RightOf="NameLabel" RelativePanel.AlignBottomWith="NameLabel" Margin="10,10,10,10" />
                    </RelativePanel>
                    <RelativePanel Grid.Row="1">
                        <TextBlock x:Name="SizeLabel" FontSize="12" x:Uid="SizeLabel"/>
                        <TextBlock x:Name="ObjectSize" FontSize="12" Margin="10,10,10,10" Text="{Binding Path=ObjectSize}" RelativePanel.RightOf="SizeLabel" RelativePanel.AlignBottomWith="SizeLabel"/>
                    </RelativePanel>
                    <RelativePanel Grid.Row="2">
                        <TextBlock x:Name="ObjectModificationDateLabel" FontSize="12" x:Uid="ModificationDateLabel" Margin="10,10,10,10" HorizontalAlignment="Right"/>
                        <TextBlock x:Name="ObjectModificationDateTextBlock" FontSize="12" Text="{Binding ObjectLastModificationDate}" Margin="10,10,10,10" RelativePanel.RightOf="ObjectModificationDateLabel" RelativePanel.AlignBottomWith="ObjectModificationDateLabel" HorizontalAlignment="Right"/>
                    </RelativePanel>

                </Grid>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

这里是更新ListBoxItem的代码:(每当用户点击父ListBox中的新项时调用)

private void UpdateListBoxWithSelectedItems()
    {
        List<S3ObjectInfoHolder> currentList = (List<S3ObjectInfoHolder>)ObjectListBox.ItemsSource; //Getting the total elements being displayed in the current child list box
        foreach (S3ObjectInfoHolder entry in listOfObjectsToTransfer) //listOfObjectsToTransfer is the list of all the entries from all the parents selected so far. So the intersection of currentList with this list gives the ones that are to be colored on UI so as the user knows the items are still selected
        {
            for (int i = 0; i < currentList.Count(); i++)
            {
                if (currentList[i].ObjectName.Equals(entry.ObjectName))
                {
                    ObjectListBox.SelectedItems.Add(entry);
                    Debug.WriteLine("Selected Item:" + entry.ObjectName);
                }
            }
        }
        List<object> selectedItem = ObjectListBox.SelectedItems.ToList();
        foreach (object item in selectedItem)
        {
            ListBoxItem selectedListBoxItem = ObjectListBox.ItemContainerGenerator.ContainerFromItem((S3ObjectInfoHolder)item) as ListBoxItem;
            if(selectedListBoxItem!=null) selectedListBoxItem.Background = new SolidColorBrush(Colors.Turquoise);
        }
    }

selectedListBoxItem!=null 失败,并跳过该行 . 最后,这是绑定类的代码:

class S3ObjectInfoHolder
{
    private string key;
    private string bucketName;
    private long size;
    private string isObjectAFolder;
    private string objectLastModificationDate;

    public S3ObjectInfoHolder(string key, string bucketName)
    {
        this.key = key;
        this.bucketName = bucketName;
        isObjectAFolder = isObjectAFolderOrAFile(key);
    }

    private string isObjectAFolderOrAFile(string key)
    {
        if (key[key.Length - 1] == '/')
        {
            return "is a Folder";
        }
        else
        {
            return "is a File";
        }
    }

    public string ObjectName
    {
        get
        {
            return key;
        }

        set
        {
            key = value;
        }
    }

    public long ObjectSize
    {
        get
        {
            return size;
        }

        set
        {
            size = value;
        }
    }

    public string ObjectIsFolder
    {
        get
        {
            return isObjectAFolder;
        }

        set
        {
            isObjectAFolder = value;
        }
    }

    public string BucketName
    {
        get
        {
            return bucketName;
        }

        set
        {
            bucketName = value;
        }
    }

    public string ObjectLastModificationDate
    {
        get
        {
            return objectLastModificationDate;
        }

        set
        {
            objectLastModificationDate = value;
        }
    }

}

1 回答

  • 1
    public class NotificationBase : INotifyPropertyChanged {
        protected void RaisePropertyChanged([CallerMemberName] string property = null)
        {
            var handler = PropertyChanged;
            if (handler != null) 
            { 
                handler(this, new PropertyChangedEventArgs(property)); 
            }
        }
    }
    
    public class S3ObjectInfoHolder : NotificationBase
    {
        //  ...
    
    • 将此属性添加到 S3ObjectInfoHolder
    public class S3ObjectInfoHolder : NotificationBase
    {
        //  ...
    
        private bool _isSelected = false;
        public bool IsSelected {
           get { return _isSelected; }
           set {
                if (_isSelected != value) {
                    _isSelected == value;
                    RaisePropertyChanged(nameof(IsSelected));
                }
            }
        }
    
        //  ...
    
    • 编写一个值转换器,根据布尔值在两个画笔之间进行选择 . 这可以在您的代码隐藏中,也可以在单独的文件中 .
    public class BrushChooserConverter : IValueConverter
    {
        public Brush TrueBrush { get; set; }
        public Brush FalseBrush { get; set; }
    
        public object Convert(object value, Type targetType,
            object parameter, string language)
        {
            return System.Convert.ToBoolean(value)
                        ? TrueBrush
                        : FalseBrush;
        }
    
        // No need to implement converting back on a one-way binding 
        public object ConvertBack(object value, Type targetType,
            object parameter, string language)
        {
            throw new NotImplementedException();
        }
    }
    
    • 在ListBox的资源中创建值转换器的实例,并使用转换器在数据模板中设置网格背景:
    <ListBox.Resources>
         <local:BrushChooserConverter
             x:Key="SelectedItemBrushChooser"
             TrueBrush="Turquoise"
             FalseBrush="Transparent"
             />
     </ListBox.Resources>
     <ListBox.ItemTemplate>
         <DataTemplate>
             <Grid 
                 Background="{Binding IsSelected, Converter={StaticResource SelectedItemBrushChooser}}"
                 Margin="10">
                 <Grid.RowDefinitions>
                     <!-- ... -->
    
    • 设置数据项的属性,让XAML完成工作 . 唐't mess with controls in code behind, it'总是一团糟,因为XAML控件不是那种方式使用的 . 在XAML ListBox 中,当前不可见的任何 ListBoxItem 将被重用于另一个数据项 . 这叫做"virtualization";它减少了创建的控件数量 .

    因此,当用户滚动此内容时,当具有彩色背景的项目滚动到视图外时,实际的 ListBoxItem 控件将重新用于滚动到视图中的下一个项目 . 它仍将具有相同的背景颜色 . 这是正确的吗?通常不会,并且没有任何事件可以让您介入并更正它 . 根本没有活动 . 那是因为你应该使用数据绑定而不是直接搞乱控件 .

    你为自己买了一个可怕的蠕虫病毒试图在代码隐藏中做到这一点 . 这比你知道的还要糟糕 . 但是我向你展示的数据绑定方法更加防错,所以一切都很好 .

    您应该有一个主视图模型,而不是在代码隐藏中执行此操作;我劝你研究一下 .

    所有的viewmodel实际上都是一个继承自我给你的 NotificationBase 的类,或者来自其他一些基类的类 . 视图模型的属性在更改时会引发通知,并且对于可能在将其用作 ItemsSource 时添加或删除项目的任何集合,它使用 ObservableCollection<T> 而不是 List<T> .

    S3ObjectInfoHolder ,随着我的更改,开始成为一个视图模型 . 它's not the main viewmodel. It'是一个儿童视图模型 . 您应该将 RaisePropertyChanged 调用添加到它拥有的每个属性 . 当您这样做,并且在XAML中使用带有该属性的 Binding 时,将在属性值更改时通知 Binding ,并且UI将自动更新 .

    但就目前而言,我只会告诉你如何修改你所拥有的东西 .

    我不知道你为什么要比较名字 . 也许 currentList[i]entry 是相同的实际对象;我无法猜测 .

    private void UpdateListBoxWithSelectedItems()
    {
        List<S3ObjectInfoHolder> currentList = (List<S3ObjectInfoHolder>)ObjectListBox.ItemsSource; 
        foreach (S3ObjectInfoHolder entry in listOfObjectsToTransfer) {
            for (int i = 0; i < currentList.Count(); i++)
            {
                if (currentList[i].ObjectName.Equals(entry.ObjectName))
                {
                    ObjectListBox.SelectedItems.Add(entry);
                    entry.IsSelected = true;
                    Debug.WriteLine("Selected Item:" + entry.ObjectName);
                }
                else
                {
                    entry.IsSelected = false;
                }
            }
        }
    

    并用容器的东西摆脱第二个循环 .

相关问题