Изменение или создание своего контрола

534
23 февраля 2018, 15:00

Добрый день. Внезапно понадобился листбокс, но не простой, а с доп свойствами, хотелось бы, чтобы он имел примерно такую структуру:

То есть я хочу сделать листбокс, в котором будет список объектов, у которых будет какой-то текст, картинки и тд и чтобы это все отображалось в одной строке. Какими способами можно это сделать? Желательно не сильно муторными. Спасибо

Answer 1

Простой пример на WPF с биндингами коих в инете полно.

XAML:

<Window.DataContext>
    <local:ListBoxViewModel />
</Window.DataContext>
<ListBox ItemsSource="{Binding Items}" x:Name="ListBox">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <Image Width="50" Source="{Binding ImageSource}" />
                <TextBlock Width="200" Text="{Binding Text}" />
                <Button Content="Удалить"
                        CommandParameter="{Binding}"
                        Command="{Binding ElementName=ListBox, Path=DataContext.DeleteCommand}" />
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

ViewModel и Model:

public class ListBoxItemModel
{
    public ListBoxItemModel(string imageSource, string text)
    {
        ImageSource = imageSource;
        Text = text;
    }
    public string ImageSource { get; }
    public string Text { get; }
}
public class ListBoxViewModel
{
    public ListBoxViewModel()
    {
        Items = new ObservableCollection<ListBoxItemModel>
        {
            new ListBoxItemModel("http://www.playground.ru/img/ui/playground-main-logo-new.png", "someText1"),
            new ListBoxItemModel("http://www.playground.ru/img/ui/playground-main-logo-new.png", "someText2")
        };
        DeleteCommand = new Prism.Commands.DelegateCommand<ListBoxItemModel>
            (item => this.Items.Remove(item));
    }
    public ObservableCollection<ListBoxItemModel> Items { get; }
    public ICommand DeleteCommand { get; }
}
Answer 2

Не очень красиво и на скорую руку, но вполне рабочий пример. Итак, сперва нужно создать некоторые вспомогательные классы для реализации MVVM паттерна - без него сам не хожу в WPF и вам не советую, так что не пугайтесь - часть всей этой простыни из разряда - один раз вставил и забыл. Так же можно использовать какой-либо MVVM-фреймворк - где все это (И даже больше) есть, но я считаю, что для начала лучше в рукопашную...

"База" для ViewModel - ViewModelBase.cs:

public class ViewModelBase : INotifyPropertyChanged
    {
        protected void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            var handler = PropertyChanged;
            handler?.Invoke(this, e);
        }
        protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
        }
        public event PropertyChangedEventHandler PropertyChanged;
    }

Два класса команд (с параметром и без - целиком стащил с какого-то сайта побыстрому):

   public class RelayCommand<T> : ICommand
    {
        #region Fields
        readonly Action<T> _execute;
        readonly Predicate<T> _canExecute;
        #endregion // Fields
        #region Constructors
        public RelayCommand(Action<T> execute)
            : this(execute, null)
        {
        }
        public RelayCommand(Action<T> execute, Predicate<T> canExecute)
        {
            _execute = execute ?? throw new ArgumentNullException(nameof(execute));
            _canExecute = canExecute;
        }
        #endregion // Constructors
        #region ICommand Members
        [DebuggerStepThrough]
        public bool CanExecute(object parameter)
        {
            return _canExecute?.Invoke((T)parameter) ?? true;
        }
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
        public void Execute(object parameter)
        {
            _execute((T)parameter);
        }
        #endregion // ICommand Members
    }
    public class RelayCommand : ICommand
    {
        #region Fields
        readonly Action<object> _execute;
        readonly Predicate<object> _canExecute;
        #endregion // Fields
        #region Constructors
        public RelayCommand(Action<object> execute)
            : this(execute, null)
        {
        }
        public RelayCommand(Action<object> execute, Predicate<object> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");
            _execute = execute;
            _canExecute = canExecute;
        }
        #endregion // Constructors
        #region ICommand Members
        [DebuggerStepThrough]
        public bool CanExecute(object parameter)
        {
            return _canExecute == null ? true : _canExecute(parameter);
        }
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
        public void Execute(object parameter)
        {
            _execute(parameter);
        }
        #endregion // ICommand Members
    }

Приступаем. Нам нужно окно где всё отображать. Запихиваем в MainWindow.xaml примерно такой xaml-код:

<Window
    x:Class="WPFApplication1.Views.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"
    Title="{Binding Title}"
    d:DesignHeight="388.602"
    d:DesignWidth="485.968"
    ResizeMode="CanResize"
    mc:Ignorable="d">
    <Grid x:Name="LayoutRoot">
        <ListView
            Grid.ColumnSpan="3"
            HorizontalAlignment="Stretch"
            HorizontalContentAlignment="Stretch"
            ItemsSource="{Binding ElementList}"
            ScrollViewer.HorizontalScrollBarVisibility="Disabled">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <Grid HorizontalAlignment="Stretch">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="100" />
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="Auto" />
                        </Grid.ColumnDefinitions>
                        <Image
                            Grid.Column="0"
                            Height="60"
                            Source="{Binding ImageUri}" />
                        <TextBlock
                            Grid.Column="1"
                            HorizontalAlignment="Stretch"
                            Text="{Binding Title}" />
                        <StackPanel Grid.Column="2" Orientation="Horizontal">
                            <Button Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListView}}, Path=DataContext.RemoveCommand}" CommandParameter="{Binding}">Удалить</Button>
                            <Button Command="{Binding SelectCommand}">Выбрать</Button>
                        </StackPanel>
                    </Grid>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>

К нему прилагается вью модель:

public class MainViewModel : ViewModelBase
{
    private string _title;
    public string Title
    {
        get { return _title; }
        set { _title = value;
            OnPropertyChanged();
        }
    }
    public ObservableCollection<Element> ElementList { get; set; }
    public MainViewModel()
    {
        ElementList = new ObservableCollection<Element>();
    }

    private RelayCommand<Element> _removeCommand;
    public RelayCommand<Element> RemoveCommand => _removeCommand ??
                                                 (_removeCommand = new RelayCommand<Element>(element =>
                                                 {
                                                     ElementList.Remove(element);
                                                 }));
}

Далее нужна вьюмодель для элементов списка:

public class Element : ViewModelBase
    {
        private string _title;
        private Uri _imageUri;
        public string Title
        {
            get { return _title; }
            set
            {
                _title = value;
                OnPropertyChanged();
            }
        }
        private RelayCommand _selectCommand;
        public RelayCommand SelectCommand => _selectCommand ??
                                            (_selectCommand = new RelayCommand(obj =>
                                            {
                                                MessageBox.Show(Title, "Вы выбрали");
                                            }));
        public Uri ImageUri
        {
            get { return _imageUri; }
            set
            {
                _imageUri = value;
                OnPropertyChanged();
            }
        }
        public Element(string title)
        {
            Title = title;
        }
    }

Далее нам нужно все это связать. Идем в App.xaml.cs и прописываем в OnStartap:

protected override void OnStartup(StartupEventArgs e)
        {
            var mainVm = new MainViewModel {Title = "Главное окно"};
            mainVm.ElementList.Add( new Element("1") {ImageUri =new Uri(@"http://steshka.ru/wp-content/uploads/2015/03/cifra_1_2.jpg") }); 
            mainVm.ElementList.Add(new Element("два") { ImageUri = new Uri(@"http://www.raskraska.ru/zifra_digital/img/dva_yellow.gif") });
            mainVm.ElementList.Add(new Element("Третий элемент") { ImageUri = new Uri(@"http://izgotovlenie-trafaretov.ru/wp-content/uploads/2014/11/trafaret-cifra-3-tip1.jpg") });
            var mw = new MainWindow {DataContext = mainVm};
            mw.Show();
            base.OnStartup(e);
        }

В принципе если я ничего не забыл - все должно заработать.

READ ALSO
RequireJS и плагины jQuery

RequireJS и плагины jQuery

Я только начал разбираться с RequireJS, но у меня появился уже вопросДопустим я имею два плагина для jQuery: a

209
Javascript вернуться в начало кода

Javascript вернуться в начало кода

Для того чтобы снова ввести число, нужно обновить страничкуМожно ли сделать, чтобы после неверного ответа программа сама снова предлагала...

318
Цикл while является устаревшим? [требует правки]

Цикл while является устаревшим? [требует правки]

В Уроках Javascript с нуляУрок 6 - Циклы утверждается, что этот цикл устаревший (между 13 и 15 минутами)

220