简体   繁体   中英

C# Wpf drawing with Combobox

I need help with drawing in my combobox. I want to make a combobox of colors for picking. I found some stuff on the internet but none of them is working. So far I have this :

 private void MyComb_DrawItem(object sender, DrawItemEventArgs e)
        {
            Graphics g = e.Graphics;
            System.Drawing.Rectangle rect = e.Bounds;
            ColorConverter converter = new ColorConverter();
             if (e.Index >= 0)
             {
             string n = ((ComboBox)sender).Items[e.Index].ToString();
              System.Drawing.Color c = (System.Drawing.Color)converter.ConvertFromString(n);
                SolidBrush b = new SolidBrush(c);
              g.FillRectangle(b, rect.X + 110, rect.Y + 5,
                  rect.Width - 10, rect.Height - 10);
          }

        }

This is my drawItem method

<Grid>
        <ComboBox x:Name="MyComb" HorizontalAlignment="Left" Margin="66,81,0,0" VerticalAlignment="Top" Width="120" />

    </Grid>

This is definition of combobox

Type colorType = typeof(System.Drawing.Color);
            PropertyInfo[] propInfoList = colorType.GetProperties(BindingFlags.Static |
                                                                  BindingFlags.DeclaredOnly | BindingFlags.Public);
            foreach (PropertyInfo c in propInfoList)
            {
                MyComb.Items.Add(c.Name);

            }

And here I am filling combobox with colors names and then I want to fill to combox with real colors according to the colors names.. But my draw item method is never called. I tried to create some DrawItem handler but, my combobox have no such thing... Then I read something about setting a DrawMode property of combobox, but my combobox doesn't that kind of property at all... I am using net framework v.4.6.1 Can please anyone tell me, what am I missing ?

Thank you very much

The biggest problem you're having is that you're trying to use code examples that were written for the Winforms API, even though you are using the WPF API. For future reference, you really need to be more careful about identifying the context of tutorials and other resources you find online, to make sure they actually apply to your scenario.

As it happens, we have a number of related questions on Stack Overflow already:

WPF ComboBox as System.Windows.Media.Colors >
WPF - Bind ComboBox Item Foreground to Its Value
Very simple color picker made of combobox

These are all potentially useful to you, but are all based on the answer to this question:

How can I list colors in WPF with XAML?

Which was originally about just displaying the names of colors, and so took a short-cut, using the <ObjectDataProvider/> element in XAML. This led to the need to use a converter in the other questions, to convert either from a string value or a PropertyInfo instance to the appropriate color or brush.

In fact, if your code is already written to use some type of MVVM approach, and especially since you've already written code-behind to retrieve the color values from the Colors type, or at least tried to (one of the problems in your code is that you are using the Winforms Color type instead of the WPF Colors type…again, in Winforms that works fine, but the WPF API follows the Code Analysis/FxCop rules more closely, and the named colors are in the Colors type), it makes sense to just stick with that and provide a direct view model data structure to which you can bind.

In this approach, rather than providing a procedural implementation of the item drawing, you provide in XAML a declarative implementation describing what each item should look like.

Here is an example…

First, some simple view model data structures:

// Very simple container class
class ColorViewModel
{
    public Brush Brush { get; }
    public string Name { get; }

    public ColorViewModel(Brush brush, string name)
    {
        Brush = brush;
        Name = name;
    }
}

// Main view model, acts as the data context for the window
class MainViewModel : INotifyPropertyChanged
{
    public IReadOnlyList<ColorViewModel> Colors { get; }

    private Brush _selectedColor;
    public Brush SelectedColor
    {
        get { return _selectedColor; }
        set { _UpdateField(ref _selectedColor, value); }
    }

    public MainViewModel()
    {
        Colors = typeof(Colors).GetProperties(BindingFlags.Static | BindingFlags.Public)
            .Select(p => new ColorViewModel(new SolidColorBrush((Color)p.GetValue(null)), p.Name))
            .ToList().AsReadOnly();
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void _UpdateField<T>(ref T field, T newValue, [CallerMemberName] string propertyName = null)
    {
        if (!EqualityComparer<T>.Default.Equals(field, newValue))
        {
            field = newValue;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

With those in hand, the XAML is straight-forward:

<Window x:Class="TestSO47850587ColorComboBox.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:l="clr-namespace:TestSO47850587ColorComboBox"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">

  <Window.DataContext>
    <l:MainViewModel/>
  </Window.DataContext>

  <StackPanel>
    <ComboBox Width="100" ItemsSource="{Binding Colors}"
              HorizontalAlignment="Left" Grid.IsSharedSizeScope="True"
              SelectedValuePath="Brush" SelectedValue="{Binding SelectedColor}">
      <ComboBox.ItemTemplate>
        <DataTemplate DataType="{x:Type l:ColorViewModel}">
          <Grid>
            <Grid.ColumnDefinitions>
              <ColumnDefinition Width="Auto" SharedSizeGroup="ComboBoxItem"/>
            </Grid.ColumnDefinitions>
            <TextBlock Text="{Binding Name}" Background="{Binding Brush}" HorizontalAlignment="Stretch"/>
          </Grid>
        </DataTemplate>
      </ComboBox.ItemTemplate>
    </ComboBox>

    <Rectangle HorizontalAlignment="Stretch" Height="24" Fill="{Binding SelectedColor}"/>
  </StackPanel>
</Window>

The above displays the color names, using the actual color as the background. If all you really want is a rectangle, then you can replace the <TextBlock/> element with a <Rectangle/> element, binding to its Fill property. Naturally, you can achieve other visual effects, such as a rectangle with a margin. It's just a matter of configuring your data template according to your need.

The main point here is that you should embrace the data binding approach that defines good WPF programming, and that you should definitely not mistake Winforms code examples for WPF. :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM