0

Нужно покрасить строки DataGrid зависимости от условия в методе IsValidName(). Для покраски был создан конвертер по имени RowToBrushConverter конвертора унаследован от базового ConverterBase. В свойстве DataGrid.RowStyle задаю стиль для DataGridRow где буду изменять цвет строки.

Проблема: в том что красит частично и только при скроллинге DataGrid.

Как должно работать: если условие верно IsValidName() красит в белый цвет если нет тогда красный.

DataGrid.RowStyle

           <DataGrid.RowStyle>
                <Style TargetType="DataGridRow">
                    <Setter Property="ToolTip">
                        <Setter.Value>
                            <StackPanel>
                                <TextBlock Text="{Binding ToolTip}" />
                            </StackPanel>
                        </Setter.Value>
                    </Setter>
                    <Setter Property="Background" Value="{Binding Converter={StaticResource RowToBrush}}"/>
                </Style>
            </DataGrid.RowStyle>

ConverterBase

    abstract class ConverterBase : MarkupExtension, IValueConverter
    {
        public abstract object Convert(object value, Type targetType, object parameter, CultureInfo culture);
        public virtual object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotImplementedException();
        public override object ProvideValue(IServiceProvider serviceProvider) => this;
    }

RowToBrushConverter

    class RowToBrushConverter : ConverterBase
    {
        public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (targetType != typeof(Brush))
            {
                return null;
            }
            var FileName = (FileName)value;
            return !FileName.IsValidName() ? Brushes.Red : Brushes.White;
        }
    }

FileName

public class FileName:ModelBase
    {
        private System.IO.FileInfo Info;
        private string Directory;
    private string original;
    public string Original
    {
        get { return original; }
        set 
        { 
            original = value;
            OriginalImage = GetBitmapSource(GetIcon());
            OnPropertyChanged(&quot;Original&quot;);
        }
    }
    private BitmapSource originalImage;
    public BitmapSource OriginalImage
    {
        get { return originalImage; }
        set
        {
            originalImage = value;
            OnPropertyChanged(&quot;OriginalImage&quot;);
        }
    }


    private string modified;
    public string Modified
    {
        get { return modified; }
        set
        {
            modified = value;
            ModifiedImage = GetBitmapSource(GetIconModified());
            OnPropertyChanged(&quot;Modified&quot;);


        }
    }

    public BitmapSource modifiedImage;
    public BitmapSource ModifiedImage
    {
        get { return modifiedImage; }
        set
        {
            modifiedImage = value;
            OnPropertyChanged(&quot;ModifiedImage&quot;);
        }
    }

    public string toolTip { get; set; }
    public string ToolTip
    {
        get { return toolTip; }
        set
        {
            toolTip = value;
            OnPropertyChanged(&quot;ToolTip&quot;);
        }
    }






    public BitmapSource GetBitmapSource(Bitmap bitmap)
    {
        BitmapSource bitmapSource = Imaging.CreateBitmapSourceFromHBitmap
        (
            bitmap.GetHbitmap(),
            IntPtr.Zero,
            Int32Rect.Empty,
            BitmapSizeOptions.FromEmptyOptions()
        );

        return bitmapSource;
    }



    //Название каталога не должно заканчиваться\
    public FileName(string directory, string original, string modified)
    {
        Directory = directory.TrimEnd('\\');

        Original = original;
        Modified = modified;
        ToolTip= ParentDirectory() + @&quot;\&quot;+ Original;

    }


    public FileName(FileName name, bool swap = false)
    {
        Directory = name.Directory.TrimEnd('\\');

        Original = name.Original;
        Modified = name.Modified;

        if (swap) Swap();
    }

    public void Reset()
    {
        Modified = Original;
    }

    public bool IsValidName()
    {
        if (string.IsNullOrEmpty(Modified))
        {
            return false;
        }

        bool result = Modified.IndexOfAny(Path.GetInvalidFileNameChars()) == -1;
        return result;
    }

    public void Swap()
    {
        var temp = Original;
        Original = Modified;
        Modified = temp;
    }

    public string GetExtension()
    {
        return Path.GetExtension(Original);
    }

    public string GetModifiedNameWithoutExtension()
    {
        return Path.GetFileNameWithoutExtension(Modified);
    }

    public string ParentDirectory()
    {
        var parts = Directory.Split('\\');
        return parts[parts.Length - 1];
    }

    public string FullPath()
    {
        return Directory + &quot;\\&quot; + Original;
    }

    public string FullPathModified(string newDirectory = null)
    {
        if (newDirectory != null) return newDirectory + &quot;\\&quot; + Modified;
        return Directory + &quot;\\&quot; + Modified;
    }



    public Bitmap GetIcon()
    {
        return Common.ShellIcon.GetBitmapIcon(FullPath());
    }

    public Bitmap GetIconModified()
    {
        return Common.ShellIcon.GetBitmapIcon(FullPathModified());
    }

    private void LoadFileInfo()
    {
        if (Info == null)
            Info = new System.IO.FileInfo(this.FullPath());
    }

    public string CreationDate()
    {
        LoadFileInfo();

        return Info.CreationTime.ToShortDateString() + &quot; &quot; + Info.CreationTime.ToShortTimeString();
    }

    public string LastWriteDate()
    {
        LoadFileInfo();

        return Info.LastWriteTime.ToShortDateString() + &quot; &quot; + Info.LastWriteTime.ToShortTimeString();
    }

    //trim file length to fit a unit
    private double TrimSize(double length, int n = 0)
    {
        for (int i = 0; i &lt; n; i++) length /= 1024.0;         //divide n times by 1024            
        return Math.Round(Convert.ToDouble(length), 2);     //convert to double and round to 2 decimal places
    }

    public string ReadableFileSize()
    {
        LoadFileInfo();

        long length = Info.Length;

        if (length &lt; Math.Pow(1024, 1)) return length + &quot; B&quot;;               //lower than 1kb
        if (length &lt; Math.Pow(1024, 2)) return TrimSize(length, 1) + &quot; KB&quot;; //lower than 1mb
        if (length &lt; Math.Pow(1024, 3)) return TrimSize(length, 2) + &quot; MB&quot;; //lower than 1gb
        if (length &lt; Math.Pow(1024, 4)) return TrimSize(length, 3) + &quot; GB&quot;; //lower than 1tb

        return length + &quot; TB&quot;;                                              //return size in tb    
    }
}

Как работает

Когда я делаю имена файлов " ",все равно некоторые строки не окрашиваться и если я скроллить не буду окраска не будет совсем происходить. введите сюда описание изображения

Как должно работать

Если имена файлов не " " введите сюда описание изображения

Если имена файлов " " введите сюда описание изображения

Vladimir
  • 403
  • 1
    Вы делаете много лишнего. Сделайте свойство, публичное, которое будет вызывать INPC, пусть это будет bool. Далее, в set того свойства, к которому надо применять эту проверку (имя?), добавляйте вызов вашей логики проверки, чтоб она изменила значение bool свойства. Все, дальше просто сделайте триггер, который задаст цвет элементу. – EvgeniyZ May 18 '21 at 10:14
  • @EvgeniyZ Как я понял например сделать свойство IsValidName и конвертировать потом из bool в цвет чтобы покрасить строку. – Vladimir May 18 '21 at 11:29
  • Зачем вам конвертировать что либо? Я ведь сказал "триггер", цвет - это обязанность View слоя, XAML, вот пусть он и разбирается что ему выводить. – EvgeniyZ May 18 '21 at 11:31
  • @EvgeniyZ я понял, какой триггер есть в DataGridRow когда метаться строка ? – Vladimir May 18 '21 at 11:39
  • 1
    Зачем вам триггер когда метаться строка DataGridRow? Вы явно меня не поняли. У вас должно быть свойство bool IsValidName, которое вызывает INPC (обновление интерфейса), в XAML у вас должен быть триггер, который привязан к этому свойству, что-то на подобии <Style.Triggers><DataTrigger Binding = "{Binding IsValidName}" Value = "true"><Setter Property="Background" Value="Red"/></DataTrigger></Style.Triggers>, все, это все решение вашей задачи. При изменении IsValidName, будет меняться и цвет (True = Red, False = default). – EvgeniyZ May 18 '21 at 11:43
  • Вот вам два примера: простой, c анимацией. Оба используют один и тот же принцип, который нужен и вам. Заметьте, ни единого конвертора и цвета в коде, лишь привязки и XAML. – EvgeniyZ May 18 '21 at 11:46
  • Cпасибо за потраченное время сейчас буду разбираться. – Vladimir May 18 '21 at 11:51

1 Answers1

0

Отдельное спасибо за наставленные EvgeniyZ.

DataGrid.RowStyle

   <DataGrid.RowStyle>
        <Style TargetType="DataGridRow">
            <Setter Property="ToolTip">
                <Setter.Value>
                    <StackPanel>
                        <TextBlock Text="{Binding ToolTip}" />
                    </StackPanel>
                </Setter.Value>
            </Setter>
            <Setter Property="Background" Value="White"/>
            <Style.Triggers>
                <DataTrigger Binding = "{Binding IsValidNameFile}" Value = "true">
                    <Setter Property="Background" Value="Red"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </DataGrid.RowStyle>

FileName

public class FileName:ModelBase
    {
        private System.IO.FileInfo Info;
        private string Directory;
    private string original;
    public string Original
    {
        get { return original; }
        set 
        { 
            original = value;
            OriginalImage = GetBitmapSource(GetIcon());
            OnPropertyChanged(&quot;Original&quot;);
        }
    }
    private BitmapSource originalImage;
    public BitmapSource OriginalImage
    {
        get { return originalImage; }
        set
        {
            originalImage = value;
            OnPropertyChanged(&quot;OriginalImage&quot;);
        }
    }


    private string modified;
    public string Modified
    {
        get { return modified; }
        set
        {
            modified = value;
            ModifiedImage = GetBitmapSource(GetIconModified());
            OnPropertyChanged(&quot;Modified&quot;);
            IsValidNameFile = !IsValidName();
        }
    }

    private BitmapSource modifiedImage;
    public BitmapSource ModifiedImage
    {
        get { return modifiedImage; }
        set
        {
            modifiedImage = value;


            OnPropertyChanged(&quot;ModifiedImage&quot;);
        }
    }

    private string toolTip { get; set; }
    public string ToolTip
    {
        get { return toolTip; }
        set
        {
            toolTip = value;
            OnPropertyChanged(&quot;ToolTip&quot;);
        }
    }


    private bool isValidNamefile;
    public bool IsValidNameFile
    {
        get
        {
            return isValidNamefile;
        }
        set
        {
            isValidNamefile = value;
            OnPropertyChanged(&quot;IsValidNameFile&quot;);
        }
    }





    public BitmapSource GetBitmapSource(Bitmap bitmap)
    {
        BitmapSource bitmapSource = Imaging.CreateBitmapSourceFromHBitmap
        (
            bitmap.GetHbitmap(),
            IntPtr.Zero,
            Int32Rect.Empty,
            BitmapSizeOptions.FromEmptyOptions()
        );

        return bitmapSource;
    }



    //Название каталога не должно заканчиваться\
    public FileName(string directory, string original, string modified)
    {
        Directory = directory.TrimEnd('\\');

        Original = original;
        Modified = modified;
        ToolTip= ParentDirectory() + @&quot;\&quot;+ Original;

    }


    public FileName(FileName name, bool swap = false)
    {
        Directory = name.Directory.TrimEnd('\\');

        Original = name.Original;
        Modified = name.Modified;

        if (swap) Swap();
    }

    public void Reset()
    {
        Modified = Original;
    }

    public bool IsValidName()
    {
        if (string.IsNullOrEmpty(Modified)|| string.IsNullOrWhiteSpace(Modified)) return false;
        return Modified.IndexOfAny(Path.GetInvalidFileNameChars()) == -1;
    }

    public void Swap()
    {
        var temp = Original;
        Original = Modified;
        Modified = temp;
    }

    public string GetExtension()
    {
        return Path.GetExtension(Original);
    }

    public string GetModifiedNameWithoutExtension()
    {
        return Path.GetFileNameWithoutExtension(Modified);
    }

    public string ParentDirectory()
    {
        var parts = Directory.Split('\\');
        return parts[parts.Length - 1];
    }

    public string FullPath()
    {
        return Directory + &quot;\\&quot; + Original;
    }

    public string FullPathModified(string newDirectory = null)
    {
        if (newDirectory != null) return newDirectory + &quot;\\&quot; + Modified;
        return Directory + &quot;\\&quot; + Modified;
    }



    public Bitmap GetIcon()
    {
        return Common.ShellIcon.GetBitmapIcon(FullPath());
    }

    public Bitmap GetIconModified()
    {
        return Common.ShellIcon.GetBitmapIcon(FullPathModified());
    }

    private void LoadFileInfo()
    {
        if (Info == null)
            Info = new System.IO.FileInfo(this.FullPath());
    }

    public string CreationDate()
    {
        LoadFileInfo();

        return Info.CreationTime.ToShortDateString() + &quot; &quot; + Info.CreationTime.ToShortTimeString();
    }

    public string LastWriteDate()
    {
        LoadFileInfo();

        return Info.LastWriteTime.ToShortDateString() + &quot; &quot; + Info.LastWriteTime.ToShortTimeString();
    }

    //trim file length to fit a unit
    private double TrimSize(double length, int n = 0)
    {
        for (int i = 0; i &lt; n; i++) length /= 1024.0;         //divide n times by 1024            
        return Math.Round(Convert.ToDouble(length), 2);     //convert to double and round to 2 decimal places
    }

    public string ReadableFileSize()
    {
        LoadFileInfo();

        long length = Info.Length;

        if (length &lt; Math.Pow(1024, 1)) return length + &quot; B&quot;;               //lower than 1kb
        if (length &lt; Math.Pow(1024, 2)) return TrimSize(length, 1) + &quot; KB&quot;; //lower than 1mb
        if (length &lt; Math.Pow(1024, 3)) return TrimSize(length, 2) + &quot; MB&quot;; //lower than 1gb
        if (length &lt; Math.Pow(1024, 4)) return TrimSize(length, 3) + &quot; GB&quot;; //lower than 1tb

        return length + &quot; TB&quot;;                                              //return size in tb    
    }
}

Vladimir
  • 403
  • 1
    Несколько замечаний на будущее: private string toolTip { get; set; } - если вы переопределяете get и set, то лучше вам нужен некий объект, который будет источником данных. Если этот объект не используется за пределами класса и для него не надо ограничивать доступ (только чтение например), то лучше использовать приватные поля, как вы например это сделали с Modified. Далее, если свойство, обновляется через UI (пользователь написал что-то), то INPC вызывать не нужно, достаточно простого public int Val {get;set;}, но если обновляется через код, то уже для обновления UI нужен INPC – EvgeniyZ May 18 '21 at 16:43
  • 1
    Метод string ReadableFileSize() я думаю у вас вовсе лишний, переопределите ToString. Портянку из if (length < Math.Pow(1024, 1)) можно (и нужно) переделать на современный switch, язык обновляется, следите за обновлениями, это может сильно сэкономить вам времени! Всякие Convert.ToDouble - уже давно устарели, используйте тип.Parse или тип.TryParse. length + " B" => "{length} B". Вызов постоянно LoadFileInfo() такая себе затея. Directory + "\\" + Original (и аналогично) лучше делать через Path.Combine. А так молодцы, что справились с задачей) – EvgeniyZ May 18 '21 at 16:54
  • @EvgeniyZ Cпасибо за помощь и за review кода. Я очень благодарен за потраченное время. – Vladimir May 18 '21 at 18:02
  • Еще вопрос у меня Model есть логика это не нарушает правила MVVM? Может вынести ее в отдельный класс. – Vladimir May 19 '21 at 17:25
  • Ну давайте порассуждаем, что такое MVVM? Это деление проекта на слабо связанные друг с другом слои, где M - это данные (работа с базой, чтение файла, отправка запросов на сайт и др.), то есть слой, который хранит и обрабатывает в себе данные проекта. V - это интерфейс (UI), все кнопочки, анимации, цвета, окна и так далее. VM - связующий слой, который берет из M нужные данные, формирует публичные свойства для привязки, а также обрабатывает команды от UI. Вот теперь посмотрите на свой код и скажите, ваша логика к какому слою относится? – EvgeniyZ May 19 '21 at 17:33
  • Я бы весь ваш код вынес в M слой, который бы собирал и хранил в себе нужные данные, размер пусть будет просто числом (байты), а в VM я лишь дергал бы нужные методы и выводил байты в нужном формате. Но помните, что MVVM, это лишь набор рекомендаций, хотите, следуйте им, хотите, нарушайте, вы создатель своего проекта, не загоняйте себя в рамки, делайте как удобно (в рамках разумного конечно). А вообще, если прям хотите "жести", то можете посмотреть этот ответ, там весьма удобный подход модульного MVVM проекта, который легко проектируется и расширяется. – EvgeniyZ May 19 '21 at 17:36
  • @EvgeniyZ Cпасибо за информацию. Это очень полезная инфа думаю еще кому-то пригодиться кто прочитает эту пост) – Vladimir May 19 '21 at 17:52