WPF 使用Popup和TreeView实现树状下拉框

效果如图

单选

<Grid Margin="15,5,0,15">
    <Grid.RowDefinitions>
        <RowDefinition Height="30" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <TextBox Grid.Row="0" x:Name="singleHeader" Width="300" Height="30" PreviewMouseDown="singleHeader_PreviewMouseDown" HorizontalAlignment="Left" />
    <Popup Grid.Row="1" x:Name="singlePopup" AllowsTransparency="True" IsOpen="False">
        <TreeView x:Name="singleTree" ItemsSource="{Binding }" Initialized="singleTree_Initialized" SelectedItemChanged="singleTree_SelectedItemChanged">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                    <StackPanel Orientation="Horizontal">
                        <TextBlock VerticalAlignment="Center" FontSize="18" Text="{Binding Name}" Margin="2,0,0,0"></TextBlock>
                    </StackPanel>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
    </Popup>
</Grid>
private void singleTree_Initialized(object sender, EventArgs e)
{
    singleDataContext = new ObservableCollection<AdministrationViewModel>();
    var beijing = new AdministrationViewModel() { Name = "北京市", Id = Guid.NewGuid().ToString() };
    beijing.Children.Add(new AdministrationViewModel() { Name = "朝阳区", Id = Guid.NewGuid().ToString() });
    beijing.Children.Add(new AdministrationViewModel() { Name = "海淀区", Id = Guid.NewGuid().ToString() });
    beijing.Children.Add(new AdministrationViewModel() { Name = "通州区", Id = Guid.NewGuid().ToString() });
    singleDataContext.Add(beijing);
    var guangdong = new AdministrationViewModel() { Name = "广东省", Id = Guid.NewGuid().ToString() };
    guangdong.Children.Add(new AdministrationViewModel() { Name = "汕尾市", Id = Guid.NewGuid().ToString() });
    guangdong.Children.Add(new AdministrationViewModel() { Name = "中山市", Id = Guid.NewGuid().ToString() });
    var guangzhou = new AdministrationViewModel() { Name = "广州市", Id = Guid.NewGuid().ToString() };
    guangzhou.Children.Add(new AdministrationViewModel() { Name = "越秀区", Id = Guid.NewGuid().ToString() });
    guangzhou.Children.Add(new AdministrationViewModel() { Name = "海珠区", Id = Guid.NewGuid().ToString() });
    guangzhou.Children.Add(new AdministrationViewModel() { Name = "番禺区", Id = Guid.NewGuid().ToString() });
    guangdong.Children.Add(guangzhou);
    singleDataContext.Add(guangdong);

    var trv = sender as TreeView;
    trv.DataContext = singleDataContext;
}

private void singleTree_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
    var trv = sender as TreeView;
    var trvItem = trv.SelectedItem as AdministrationViewModel;
    //这里是否被选择的条件为是否为叶子结点,也可以使用AdministrationViewModel中的属性灵活控制
    if (trvItem.Children.Count != 0) return;
    singleHeader.Text = trvItem.Name.ToString();
    singlePopup.IsOpen = false;
}

private void singleHeader_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
    singlePopup.Placement = System.Windows.Controls.Primitives.PlacementMode.RelativePoint;
    singlePopup.VerticalOffset = singleHeader.Height;
    singlePopup.StaysOpen = true;
    singlePopup.Height = singleTree.Height;
    singlePopup.Width = singleHeader.Width;
    singlePopup.IsOpen = true;
}

多选

<Grid Margin="15,5,0,15">
    <Grid.RowDefinitions>
        <RowDefinition Height="30" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <TextBox Grid.Row="0" x:Name="multiHeader" Width="300" Height="30" PreviewMouseDown="multiHeader_PreviewMouseDown" HorizontalAlignment="Left" />
    <Popup Grid.Row="1" x:Name="multiPopup" AllowsTransparency="True" IsOpen="False">
        <StackPanel Orientation="Vertical">
            <TreeView x:Name="multiTree" ItemsSource="{Binding }" Initialized="multiTree_Initialized">
                <TreeView.ItemTemplate>
                    <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                        <StackPanel Orientation="Horizontal">
                            <CheckBox IsChecked="{Binding IsChecked,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" IsEnabled="{Binding IsCanChecked,Mode=OneWay}" Checked="CheckBox_CheckedOrUncheck" Unchecked="CheckBox_CheckedOrUncheck"></CheckBox>
                            <TextBlock VerticalAlignment="Center" FontSize="18" Text="{Binding Name}" Margin="2,0,0,0"></TextBlock>
                        </StackPanel>
                    </HierarchicalDataTemplate>
                </TreeView.ItemTemplate>
            </TreeView>
            <Button Content="Close(关闭)" Click="CloseMultiPopup_Click"></Button>
        </StackPanel>
    </Popup>
</Grid>
private void multiHeader_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
    multiPopup.Placement = System.Windows.Controls.Primitives.PlacementMode.RelativePoint;
    multiPopup.VerticalOffset = multiHeader.Height;
    multiPopup.StaysOpen = true;
    multiPopup.Height = multiTree.Height;
    multiPopup.Width = multiHeader.Width;
    multiPopup.IsOpen = true;
}

private void multiTree_Initialized(object sender, EventArgs e)
{
    multiDataContext = new ObservableCollection<AdministrationViewModel>();
    var beijing = new AdministrationViewModel() { Name = "北京市", Id = Guid.NewGuid().ToString() };
    beijing.Children.Add(new AdministrationViewModel() { Name = "朝阳区", Id = Guid.NewGuid().ToString(),IsCanChecked=true });
    beijing.Children.Add(new AdministrationViewModel() { Name = "海淀区", Id = Guid.NewGuid().ToString(), IsCanChecked = true });
    beijing.Children.Add(new AdministrationViewModel() { Name = "通州区", Id = Guid.NewGuid().ToString() });
    multiDataContext.Add(beijing);
    var guangdong = new AdministrationViewModel() { Name = "广东省", Id = Guid.NewGuid().ToString() };
    guangdong.Children.Add(new AdministrationViewModel() { Name = "汕尾市", Id = Guid.NewGuid().ToString(), IsCanChecked = true });
    guangdong.Children.Add(new AdministrationViewModel() { Name = "中山市", Id = Guid.NewGuid().ToString(), IsCanChecked = true });
    var guangzhou = new AdministrationViewModel() { Name = "广州市", Id = Guid.NewGuid().ToString() };
    guangzhou.Children.Add(new AdministrationViewModel() { Name = "越秀区", Id = Guid.NewGuid().ToString(), IsCanChecked = true });
    guangzhou.Children.Add(new AdministrationViewModel() { Name = "海珠区", Id = Guid.NewGuid().ToString(), IsCanChecked = true });
    guangzhou.Children.Add(new AdministrationViewModel() { Name = "番禺区", Id = Guid.NewGuid().ToString(), IsCanChecked = true });
    guangdong.Children.Add(guangzhou);
    multiDataContext.Add(guangdong);
    var trv = sender as TreeView;
    trv.DataContext = multiDataContext;
}

private static void GetSelectList(List<AdministrationViewModel> selectList, AdministrationViewModel item)
{
    if(item.Children!=null)
    {
        foreach (var child in item.Children)
        {
            if (child.IsCanChecked && child.IsChecked)
                selectList.Add(child);
            GetSelectList(selectList, child);
        }
    }
    
}

private void CheckBox_CheckedOrUncheck(object sender, RoutedEventArgs e)
{
    var trv = multiTree;
    var data = trv.DataContext as ObservableCollection<AdministrationViewModel>;
    //获得所有被勾选的选项:这里使用IsChecked和IsCanChecked进行判断->可以根据业务改为其他的逻辑
    var selectList = new List<AdministrationViewModel>();
    foreach (var item in data)
    {
        if (item.IsCanChecked && item.IsChecked)
            selectList.Add(item);
        GetSelectList(selectList, item);
    }
    var selectStr = "";
    foreach (var item in selectList)
    {
        selectStr += item.Name + ",";
    }
    selectStr = selectStr.TrimEnd(',');
    multiHeader.Text = selectStr;
}

private void CloseMultiPopup_Click(object sender, RoutedEventArgs e)
{
    multiPopup.IsOpen = false;
}

示例代码

PopupWithTreeView

参考资料

WPF: TreeView inside a ComboBox

原文地址:https://www.cnblogs.com/Lulus/p/14042228.html