2

Во время компиляции программа создает несколько кнопок и присваивает им стиль, мне нужно изменить параметры name и autor во время компиляции, как это реализовать? вот сама кнопка

<Window x:Class="SoundPlayer.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:local="clr-namespace:SoundPlayer"
        mc:Ignorable="d"
        Title="MainWindow" MinHeight="400" MinWidth="700"
        Width="700" Height="400"
        Background="#FF212121">
    <Window.Resources>
        <Style TargetType="Button" x:Key="songStyle">
            <Style.Setters>
                <Setter Property="Height" Value="40"/>
                <Setter Property="Margin" Value="2,1,2,1"/>
                <Setter Property="OverridesDefaultStyle" Value="True"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="Button">
                            <Border Name="borderSongButton" BorderThickness="0" Background="#FF324D95">
                                <Grid>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="5"/>
                                        <ColumnDefinition Width="auto"/>
                                        <ColumnDefinition Width="5"/>
                                        <ColumnDefinition Width="*"/>
                                        <ColumnDefinition Width="5"/>
                                        <ColumnDefinition Width="30"/>
                                        <ColumnDefinition Width="5"/>
                                    </Grid.ColumnDefinitions>
                                    <TextBlock x:Name="numberTextBlock" Grid.Column="1" Text="1."
                                               FontSize="30" VerticalAlignment="Center"
                                               HorizontalAlignment="Center"
                                               Foreground="#FF9B9B9B"/>
                                    <Grid x:Name="songInfoGrid" Grid.Column="3">
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="*" MinWidth="80"/>
                                            <ColumnDefinition Width="3"/>
                                            <ColumnDefinition Width="auto" MinWidth="80" MaxWidth="150"/>
                                            <ColumnDefinition Width="3"/>
                                            <ColumnDefinition Width="auto"/>
                                        </Grid.ColumnDefinitions>
                                        <Grid.RowDefinitions>
                                            <RowDefinition Height="15"/>
                                            <RowDefinition Height="*"/>
                                        </Grid.RowDefinitions>
                                        <TextBlock x:Name="nameTitleTextBlock" Text="Название:"
                                                   VerticalAlignment="Center"
                                                   Foreground="#FF9B9B9B"/>
                                        <TextBlock x:Name="songNameTextBlock" Text="NAME"
                                                   Grid.Row="1" FontSize="25"
                                                   VerticalAlignment="Center" Foreground="#FF272727"/>
                                        <Border Grid.Column="1" Grid.RowSpan="2" Background="#FF9B9B9B" Width="1"/>
                                        <TextBlock x:Name="autorTitleTextBlock" Text="Исполнитель:"
                                                   Foreground="#FF9B9B9B" Grid.Column="2"
                                                   VerticalAlignment="Center"/>
                                        <TextBlock x:Name="autorTextBlock" Text="autor"
                                                   Grid.Column="2"  Grid.Row="1"
                                                   VerticalAlignment="Center"
                                                   FontSize="25" Foreground="#FF272727"/>
                                        <Border Grid.Column="3" Grid.RowSpan="2" Background="#FF9B9B9B" Width="1"/>
                                        <TextBlock x:Name="durationTextBlock" Text="00:00"
                                                   Grid.Column="5" Grid.RowSpan="2"
                                                   VerticalAlignment="Center"
                                                   Foreground="#FF272727" FontSize="20"/>
                                    </Grid>
                                    <Button x:Name="optionButton" Grid.Column="5" Height="30"/>
                                </Grid>
                            </Border>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style.Setters>
        </Style>
    </Window.Resources>
    <Grid x:Name="mainGrid">
        <Grid.RowDefinitions>
            <RowDefinition Height="323*"/>
            <RowDefinition Height="5*"/>
            <RowDefinition Height="60*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="300*"/>
            <ColumnDefinition Width="5*"/>
            <ColumnDefinition Width="800*"/>
            <ColumnDefinition Width="0*"/>
        </Grid.ColumnDefinitions>
        <Rectangle Fill="#FF252525" RadiusX="10" RadiusY="10"/>
        <Border x:Name="playlistBorder" Grid.Column="2" Grid.Row="0" Background="#FF242742" CornerRadius="10"/>
        <Border Grid.Column="0" Grid.Row="2" Background="#FF242742" CornerRadius="10"/>
        <Grid Grid.Column="2">
            <Grid.RowDefinitions>
                <RowDefinition Height="25"/>
                <RowDefinition Height="5"/>
                <RowDefinition Height="*"/>
                <RowDefinition Height="10"/>
            </Grid.RowDefinitions>
            <Grid x:Name="sortGrid" Grid.RowSpan="2">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="15"/>
                    <ColumnDefinition Width="150"/>
                    <ColumnDefinition Width="5"/>
                    <ColumnDefinition Width="30"/>
                    <ColumnDefinition Width="27*"/>
                    <ColumnDefinition Width="1.5*"/>
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="2*"/>
                    <RowDefinition Height="10*"/>
                    <RowDefinition Height="2*"/>
                </Grid.RowDefinitions>
                <Border Grid.ColumnSpan="6" Grid.RowSpan="3" Background="#FF3A3A3A" CornerRadius="10,10,0,0"/>
                <ComboBox x:Name="sortComboBox" Grid.Row="1" Grid.Column="1"
                          HorizontalContentAlignment="Center"
                          VerticalContentAlignment="Center">
                    <TextBlock Text="по названию"/>
                    <TextBlock Text="по длине"/>
                    <TextBlock Text="по исполнителю"/>
                </ComboBox>
                <Button x:Name="sortButton" Grid.Row="1" Grid.Column="3"/>
            </Grid>
            <ScrollViewer x:Name="playListScrollView" Grid.Row="2" VerticalScrollBarVisibility="Auto">
                <StackPanel x:Name="playListStackPanel" Grid.Row="1">
                    <Button Height="40"/>
                    <Button Height="40"/>
                    <Button Height="40"/>
                </StackPanel>
            </ScrollViewer>
        </Grid>
        <Grid Grid.Row="2" Grid.Column="0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="9*"/>
                <ColumnDefinition Width="127*"/>
                <ColumnDefinition Width="38*"/>
                <ColumnDefinition Width="9*"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="2*"/>
                <RowDefinition Height="7*"/>
                <RowDefinition Height="2*"/>
            </Grid.RowDefinitions>
            <Button Grid.Row="1" Grid.Column="2"/>
        </Grid>
        <Grid x:Name="navigationGrid" Grid.Column="2" Grid.Row="2">
            <Grid.RowDefinitions>
                <RowDefinition Height="7*"/>
                <RowDefinition Height="1*"/>
                <RowDefinition Height="10*"/>
                <RowDefinition Height="3*"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="60"/>
                <ColumnDefinition Width="70*"/>
                <ColumnDefinition Width="60"/>
            </Grid.ColumnDefinitions>
            <ScrollBar Orientation="Horizontal"  Grid.Row="0" Grid.Column="1" Height="20"/>
            <TextBlock x:Name="currentTimeTextBlock" Text="00:00" VerticalAlignment="Center" TextAlignment="Center" Foreground="#FFA8A8A8" FontSize="20"/>
            <TextBlock x:Name="maxTimeTextBlock" Text="00:00" VerticalAlignment="Center" TextAlignment="Center" Foreground="#FFA8A8A8" FontSize="20" Grid.Column="2"/>
            <Grid Grid.Row="2" Grid.Column="1">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="5*"/>
                    <ColumnDefinition Width="15*"/>
                    <ColumnDefinition Width="30*"/>
                </Grid.ColumnDefinitions>
                <Grid x:Name="playerButtonsGrid" Grid.Column="1">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="6*"/>
                        <ColumnDefinition Width="1*"/>
                        <ColumnDefinition Width="6*"/>
                        <ColumnDefinition Width="1*"/>
                        <ColumnDefinition Width="6*"/>
                    </Grid.ColumnDefinitions>
                    <Button Grid.Column="0"/>
                    <Button x:Name="pauseButton" Grid.Column="2"/>
                    <Button Grid.Column="4"/>
                </Grid>
                <Grid Grid.Column="2">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="10*"/>
                        <ColumnDefinition Width="80*"/>
                        <ColumnDefinition Width="10*"/>
                    </Grid.ColumnDefinitions>
                    <Border x:Name="volumeBorder" Grid.Column="1" Grid.ColumnSpan="2" Background="#FF242742" CornerRadius="10"/>
                    <Grid x:Name="volumeGrid" Grid.Column="1" Grid.ColumnSpan="2">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="1*"/>
                            <ColumnDefinition Width="25*"/>
                            <ColumnDefinition Width="1*"/>
                            <ColumnDefinition Width="3*"/>
                            <ColumnDefinition Width="1*"/>
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="10*"/>
                            <RowDefinition Height="25*"/>
                            <RowDefinition Height="10*"/>
                        </Grid.RowDefinitions>
                        <ScrollBar x:Name="VolumeScroll" Orientation="Horizontal" Grid.Row="1" Grid.Column="1"/>
                        <ToggleButton x:Name="switchVolumeButton" Grid.Row="1" Grid.Column="3"/>
                    </Grid>
                </Grid>
            </Grid>
        </Grid>
    </Grid>
</Window>
public MainWindow()
{
    InitializeComponent();
    AddSongButtonToPlayList();
}

public void AddSongButtonToPlayList() { Button songButton = new Button(); songButton.Style = this.Resources["songStyle"] as Style; //изменение name и autor playListStackPanel.Children.Add(songButton); }

aepot
  • 49,560
izem
  • 23
  • Предтавьте ваш код, будьте так добры. – Dmitry May 01 '21 at 14:27
  • @Дмитрий добавил, но это тестовый вариант метода, для проверки – izem May 01 '21 at 14:32
  • создает несколько кнопок и присваивает им стиль - нужен вот этот код – aepot May 01 '21 at 14:34
  • 2
    Не устану повторять. Хватит использовать WPF так, будто вы разрабатываете WinForms! Научитесь использовать базовые его механизмы, а именно XAML и привязки. Вы не должны в коде делать кнопки, вы должны в коде делать коллекцию с данными, которая будет привязана и по которой сам WPF построит UI. – EvgeniyZ May 01 '21 at 14:34
  • Покажите всю разметку со стилями и весь код, и скажите, что хотите получить, так же опишите. какую именно задачу вы решаете. Чтобы не пришлось догадываться, что вы хотите получить. Вы уехали не туда, и чтобы вас вернуть, нужно начать с начала. Я могу вам помочь в этом, но мне нужно максимум информации. – aepot May 01 '21 at 14:59
  • https://ru.stackoverflow.com/a/1275459/373567 пример привязки листбокса к коллекции – aepot May 01 '21 at 15:05
  • @aepot хочу сделать простой аудио плеер. Пользователь указывает папку, там музыка. На основе каждого трека создается кнопка для его запуска, вроде все. – izem May 01 '21 at 15:10
  • Ваша главная ошибка в том, что вы начали наводить красоту, но при этом не позаботились о функциональной части приложения. Получилась весьма сложная структура в интерфейсе, которая ничего не умеет. Еще вы перепутали ScrollBar и ProgressBar. Попробую без наворотов сделать пример. А вы уж сами его вкрутите потом в свой интерфейс. – aepot May 01 '21 at 15:37
  • 1
    @aepot хорошо, спасибо – izem May 01 '21 at 15:57

1 Answers1

1

Чтож, вы мне показали дизайн без начинки, я вам покажу начинку без дизайна.

Простой аудиоплеер в WPF

Решение может:

  • Переходить по каталогам на текущем диске
  • Добавлять MP3 файл в плейлист - двойной клик по файлу в левой панели
  • Воспроизводить файл из плейлиста - двойной клик по файлу в плейлисте или кнопка Play
  • Воспроизводить следующий файл, если текущий закончился автоматически
  • Ставить воспроизведение на паузу - кнопка Pause
  • Показывать, сколько времени прошло, продолжительность трека и прогресс воспроизведения в ProgressBar
  • Менять позицию воспроизведения при клике на прогрессбар
  • Регулировать громкость

Может приложение не много, но и не мало. Я старался сделать так, чтобы пример выглядел как можно проще, но при этом показать самые основные принципы, которые вы сможете переиспользовать при дальнейшей разработке.

Я выбрал стандартный MediaPlayer в качестве аудиодвижка для простоты примера, но он мало чего умеет. Советую присмотреться к библиотеке NAudio.

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

Прямо отвечая на ваш вопрос - как менять данные в интерфейсе из кода, отвечу, что надо сделать следующие шаги:

  • Создать класс, в котором разместить свойство, являющееся источником данных для контрола. Например в решении ниже такой класс - MainViewModel
  • Класс, содержащий нужное свойство должен реализовывать интерфейс INotifyPropertyChanged. У меня реализация интерфейса наследуется из класса NotifyPropertyChanged, представленного ниже.
  • Свойство, которое предоставляет данные должно оповещать интерфейс об изменениях данныз посредством вызова события PropertyChanged передавая в качестве аргумента своё имя. Данный механизм у меня реализован в методе OnPropertyChanged() где имя вызвавшего метод свойства автоматически подсталяется компилятором с помощью аттрибута [CallerMemberName].
  • Контрол нужно привязать к свойству с помощью Binding например <TextBlock Text="{Binding MyText}"/>
  • Чтобы Binding понял, где искать нужное свойство с данными, окну надо задать DataContext. Это сделано у меня в конструкторе окна MainWindow.

Далее можно менять значение свойства, а контрол при этом будет обновлять своё содержимое автоматически.

С первого взгляда может показаться сложным, но оно того стоит, потому что полностью избавляет от войны с контролами в C# коде. Я вообще не работаю с контролами в коде, все контролы находятся в разметке. Есть конечно в коде MediaPlayer, он контрол, но я его не использую в разметке интерфейса.

Теперь к делу

Вспомогательные классы

public class NotifyPropertyChanged : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

public class RelayCommand : ICommand { private readonly Action<object> _execute; private readonly Predicate<object> _canExecute;

public event EventHandler CanExecuteChanged
{
    add =&gt; CommandManager.RequerySuggested += value;
    remove =&gt; CommandManager.RequerySuggested -= value;
}

public RelayCommand(Action&lt;object&gt; execute, Predicate&lt;object&gt; canExecute = null)
    =&gt; (_execute, _canExecute) = (execute, canExecute);

public bool CanExecute(object parameter)
    =&gt; _canExecute == null || _canExecute(parameter);

public void Execute(object parameter)
    =&gt; _execute(parameter);

}

Структуры данных

public enum FileItemType
{
    File,
    Directory
}

public class FileItem { public FileItemType Type { get; } public string FilePath { get; } public string Name => Path.GetRelativePath(Environment.CurrentDirectory, FilePath);

public FileItem(FileItemType type, string path)
{
    Type = type;
    FilePath = path;
}

public override bool Equals(object obj) 
    =&gt; obj is FileItem item &amp;&amp; FilePath.Equals(item.FilePath);

public override int GetHashCode() 
    =&gt; FilePath.GetHashCode();

}

View Model класс

Здесь весь основной код

public class MainViewModel : NotifyPropertyChanged
{
    const string fileMask = "*.mp3";
    private readonly MediaPlayer _player = new MediaPlayer();
    private List<FileItem> _files;
    private string _currentDirectory;
    private ICommand _changeDirCommand;
    private ICommand _PlayCommand;
    private ObservableCollection<FileItem> _playList;
    private bool _isPlaying;
    private bool _isPaused = true;
    private CancellationTokenSource _cts;
    private Task _playingTask;
    private FileItem _currentTrack;
    private TimeSpan _duration;
    private TimeSpan _position;
    private ICommand _pauseResumeCommand;
public double Volume
{ 
    get =&gt; _player.Volume; 
    set =&gt; _player.Volume = value; 
}

public TimeSpan Duration
{
    get =&gt; _duration; 
    set
    {
        if (value != _duration)
        {
            _duration = value;
            OnPropertyChanged();
        }
    }
}

public TimeSpan Position
{
    get =&gt; _position;
    set
    {
        if (value != _position)
        {
            _position = value;
            OnPropertyChanged();
        }
    }
}

public FileItem CurrentTrack
{
    get =&gt; _currentTrack;
    set
    {
        _currentTrack = value;
        OnPropertyChanged();
    }
}

public bool IsPlaying
{
    get =&gt; _isPlaying;
    set
    {
        _isPlaying = value;
        OnPropertyChanged();
    }
}

public bool IsPaused
{
    get =&gt; _isPaused;
    set
    {
        _isPaused = value;

        if (_isPaused)
        {
            if (_player.CanPause)
                _player.Pause();
        }
        else
            _player.Play();

        OnPropertyChanged();
    }
}

public List&lt;FileItem&gt; Files
{
    get =&gt; _files;
    set
    {
        _files = value;
        OnPropertyChanged();
    }
}

public double Progress
{
    get
    {
        if (_player.NaturalDuration.HasTimeSpan)
        {
            Duration = _player.NaturalDuration.TimeSpan;
            Position = _player.Position;
            return Position.TotalSeconds * 100 / Duration.TotalSeconds;
        }
        return 0;
    }
    set
    {
        if (_player.NaturalDuration.HasTimeSpan)
        {
            Position = TimeSpan.FromSeconds(value * Duration.TotalSeconds / 100);
            _player.Position = Position;
            OnPropertyChanged();
        }
    }
}

public ObservableCollection&lt;FileItem&gt; PlayList
{
    get =&gt; _playList;
    set
    {
        _playList = value;
        OnPropertyChanged();
    }
}

public string CurrentDirectory
{
    get =&gt; _currentDirectory;
    set
    {
        _currentDirectory = value;
        OnPropertyChanged();
    }
}

private void LoadDirectories()
{
    CurrentDirectory = Environment.CurrentDirectory;
    var files = new List&lt;FileItem&gt;();
    if (Path.GetPathRoot(CurrentDirectory) != CurrentDirectory)
        files.Add(new FileItem(FileItemType.Directory, Path.Combine(CurrentDirectory, &quot;..&quot;)));
    files.AddRange(Directory.GetDirectories(CurrentDirectory).Select(d =&gt; new FileItem(FileItemType.Directory, d)));
    files.AddRange(Directory.GetFiles(CurrentDirectory, fileMask).Select(f =&gt; new FileItem(FileItemType.File, f)));
    Files = files;
}

public ICommand ChangeDirCommand =&gt; _changeDirCommand ??= new RelayCommand(parameter =&gt;
{
    if (parameter is FileItem item)
    {
        switch (item.Type)
        {
            case FileItemType.Directory:
                Directory.SetCurrentDirectory(item.Name);
                LoadDirectories();
                break;
            case FileItemType.File:
                PlayList.Add(item);
                break;
        }
    }
});

public ICommand PlayCommand =&gt; _PlayCommand ??= new RelayCommand(parameter =&gt;
{
    Play();
});

public ICommand PauseResumeCommand =&gt; _pauseResumeCommand ??= new RelayCommand(_ =&gt;
{
    if (!IsPlaying)
        Play();
    else
        IsPaused = !IsPaused;
}, _ =&gt; CurrentTrack != null);

private async void Play()
{
    if (CurrentTrack == null)
        return;
    if (_cts != null)
    {
        _cts?.Cancel();
        await _playingTask;
    }
    _playingTask = PlayAsync(CurrentTrack);
}

private void PlayNext()
{
    if (PlayList.Count &gt; 1)
    {
        int index = PlayList.IndexOf(CurrentTrack);
        if (index &lt; PlayList.Count - 1)
        {
            CurrentTrack = PlayList[index + 1];
            Play();
        }
        else
            IsPaused = true;
    }
    else 
        IsPaused = true;
}

private async Task PlayAsync(FileItem item)
{
    IsPlaying = true;
    _player.Open(new Uri(item.FilePath));
    IsPaused = false;
    using (_cts = new CancellationTokenSource())
    {
        try
        {
            while (true)
            {
                OnPropertyChanged(nameof(Progress));
                await Task.Delay(500, _cts.Token);
            }
        }
        catch (OperationCanceledException)
        { }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }
    _cts = null;
    IsPlaying = false;
    Progress = 0;
}

public void Start()
{
    _player.MediaEnded += (s, e) =&gt; { _cts?.Cancel(); PlayNext(); };
    PlayList = new ObservableCollection&lt;FileItem&gt;();
    LoadDirectories();
}

}

Подключение View Model к окну

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        var vm = new MainViewModel();
        DataContext = vm;
        Loaded += (s, e) => vm.Start();
    }
}

Кликабельный прогрессбар

Нужен, чтобы можно было в него тыкать и менять текущее время воспроизведения

public class EditableProgressBar : ProgressBar
{
    public EditableProgressBar() : base() 
        => Cursor = Cursors.Hand;
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
    base.OnMouseLeftButtonDown(e);
    Value = e.GetPosition(this).X * (Maximum - Minimum) / ActualWidth;
}

}

Разметка интерфейса

<Window x:Class="WpfApp2.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:local="clr-namespace:WpfApp2"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800" d:DataContext="{d:DesignInstance local:MainViewModel}">
    <Window.Resources>
        <Style TargetType="{x:Type ListBoxItem}">
            <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*"/>
            <ColumnDefinition Width="2*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <ListBox ItemsSource="{Binding Files}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock>
                        <TextBlock.Style>
                            <Style TargetType="{x:Type TextBlock}">
                                <Setter Property="Text" Value="{Binding Name}"/>
                                <Style.Triggers>
                                    <DataTrigger Binding="{Binding Type}" Value="Directory">
                                        <Setter Property="Text" Value="{Binding Name, StringFormat=[{0}]}"/>
                                    </DataTrigger>
                                </Style.Triggers>
                            </Style>
                        </TextBlock.Style>
                        <TextBlock.InputBindings>
                            <MouseBinding MouseAction="LeftDoubleClick" Command="{Binding DataContext.ChangeDirCommand, RelativeSource={RelativeSource AncestorType=ListBox}}" CommandParameter="{Binding}"/>
                        </TextBlock.InputBindings>
                    </TextBlock>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <ListBox Grid.Column="1" ItemsSource="{Binding PlayList}" SelectedItem="{Binding CurrentTrack}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Name}">
                        <TextBlock.InputBindings>
                            <MouseBinding MouseAction="LeftDoubleClick" Command="{Binding DataContext.PlayCommand, RelativeSource={RelativeSource AncestorType=ListBox}}" CommandParameter="{Binding}"/>
                        </TextBlock.InputBindings>
                    </TextBlock>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <Grid Margin="10" Grid.Row="1" Grid.Column="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="50"/>
                <ColumnDefinition/>
                <ColumnDefinition Width="50"/>
            </Grid.ColumnDefinitions>
            <TextBlock Text="{Binding Position, StringFormat=mm\\:ss}" Padding="5" HorizontalAlignment="Center"/>
            <local:EditableProgressBar Grid.Column="1" Value="{Binding Progress}"/>
            <TextBlock Grid.Column="2" Text="{Binding Duration, StringFormat=mm\\:ss}" Padding="5" HorizontalAlignment="Center"/>
        </Grid>
        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <Slider VerticalAlignment="Center" Margin="10" Value="{Binding Volume}" Maximum="1" LargeChange="0.1" SmallChange="0.01"/>
            <Button Grid.Column="1" FontFamily="Segoe MDL2 Assets" Margin="10" FontSize="20" Command="{Binding PauseResumeCommand}" Padding="10 0">
                <Button.Style>
                    <Style TargetType="{x:Type Button}">
                        <Setter Property="Content" Value="&#xF5B0;"/>
                        <Style.Triggers>
                            <MultiDataTrigger>
                                <MultiDataTrigger.Conditions>
                                    <Condition Binding="{Binding IsPlaying}" Value="True"/>
                                    <Condition Binding="{Binding IsPaused}" Value="False"/>
                                </MultiDataTrigger.Conditions>
                                <MultiDataTrigger.Setters>
                                    <Setter Property="Content" Value="&#xF8AE;"/>
                                </MultiDataTrigger.Setters>
                            </MultiDataTrigger>
                        </Style.Triggers>
                    </Style>
                </Button.Style>
            </Button>
        </Grid>
    </Grid>
</Window>

Данный ответ написан под музыку, которую играл этот плеер.

введите сюда описание изображения

Решение в сборе на Яндекс.Диске - https://disk.yandex.ru/d/YYS4u6M7viiLug

aepot
  • 49,560
  • Хороший ответ, хоть и с нарушениями MVVM, ведь MediaPlayer, это View. Конечно, для понимания "и так сойдет", но все же. И кстати по поводу понимания, вот уверен, что новичок, который пытался играться со стилями кнопки, чтоб изменить так текст, вряд ли что-либо поймет без объяснения каждой строки, может стоит больше пояснений делать, раз на то пошло? – EvgeniyZ May 01 '21 at 20:32
  • @EvgeniyZ да я уже к концу реализации только понял, что это контрол. :) Я еще сейчас прямой ответ на вопрос из топика допишу. – aepot May 01 '21 at 20:38
  • Тут самое главное пояснения для автора вопроса сделать, ибо сейчас, как по мне, этот ответ равнозначен тому, если бы мы просто дали ссылку на GitHub и сказали бы "разбирайся", ведь в ответе что? Код и "что этот код может". С нарушением то фиг с ним, его если автор поймет, сам исправит, ибо чего там, простая инверсия, чтоб VM отдавал событие, на которое подпишется V, но вот объяснение, ну не поймет человек не знающий всех тонкостей... – EvgeniyZ May 01 '21 at 20:44
  • @EvgeniyZ допилено – aepot May 01 '21 at 20:47
  • @EvgeniyZ сижу голову ломаю, интересно стало, как сделать, чтобы при тыке в прогресс бар он перескакивал на место, где я ткнул. В смысле, я хочу поймать тык, высчитать из него время, дать это плееру, чтобы он перестроился на новое время, в результате чего прогресс бар бы встал в нужный прогресс. Просто ищу способ захвата координат тыка мышкой. Или это только через обработчик собития сделать можно? – aepot May 01 '21 at 21:37
  • 1
    А разве это не функционал слайдера? – EvgeniyZ May 01 '21 at 21:41
  • @EvgeniyZ решил наследованием от прогрессбара, так как слайдер дал не совсем то поведение, которое я ожидал, и привести к нужному поведению у меня не получилось. Обновил решение. Попутно прикрутил регулятор громкости. – aepot May 02 '21 at 00:42
  • 1
    @aepot огромное спасибо за развернутый ответ. Думаю, благодаря таким людям, как вы комьюнити и живет. Я сейчас читаю Троелсона, прошел пару глав по wpf, но не дошел до mvvm и не углубился в привязки, получается поторопился, буду наверстывать. Еще раз спасибо. – izem May 02 '21 at 06:09
  • @ИльяВолков вот еще пример, может оказаться полезным. – aepot May 02 '21 at 10:32
  • @aepot О каком именно поведении идет речь? Допустим, набросал на скорую руку такой стиль, который +- повторяет стандартный прогресс бар без анимаций, получится такой эффект, вроде то, что и требовалось, не? Для чего просто изобретать новый контрол не пойму. – EvgeniyZ May 02 '21 at 11:19
  • @EvgeniyZ похоже на правду, да. А у меня такое не получилось. Оно при клике прыгало не в точку, где ткнул, а на определенный шаг. Кстати, я еще подумал, потом еще подумал, и решил, что драггинг мышкой прогрессбара здесь - лишнее. Не совсем понятно, как должен вести себя плеер при драггинге. Явно не должен не перестраиваться в реалтайме, иначе будет много мусора в колонках. Поэтому пожалуй оставлю решение с наследованием. Но всё равно спасибо, уже разбираю ваше решение. Однозначно полезно, и после допила можно получить идеальное в плане UX решение. – aepot May 02 '21 at 11:31
  • 1
    @aepot Чтоб переносило в нужную точку, должно быть выполнено несколько условий IsMoveToPointEnabled = true и должен быть верно написанный Track. при драггинге - я бы ставил на паузу (либо не выполнял каких-либо действий) до тех пор, пока не будет отжата кнопка мыши. – EvgeniyZ May 02 '21 at 11:44