Laden...

Forenbeiträge von jbrown Ingesamt 23 Beiträge

21.04.2025 - 06:41 Uhr

Wenn du ein DataTable als Source verwendest, wovon ich ausgehe, dann sind die Einträge im Dropdown DataRowView Einträge. Damit könntest du vermutlich so vorgehen:

private void CbRole_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (cbRole.SelectedItem is DataRowView row)
    {
        var id = row["ID"]?.ToString(); // oder: row.Row["ID"]
        var description = row["Description"]?.ToString();

        MessageBox.Show($"ID: {id}, Description: {description}");
    }
}

Falls du SelectedItem wirklich binden möchtest, brauchen wir sicher ein paar Zusatzinformationen zu der Eigenschaft "ID" die gebunden werden soll. So wie bisher wird das nicht funktionieren:

SelectedItem="{Binding ID, Mode=TwoWay}" SelectedValue="{Binding ID}" SelectedValuePath="{Binding ID}"
08.03.2025 - 03:20 Uhr

Wie hier so oft gepredigt wird, solltest du dem WPF Konzept folgen und mit ViewModels arbeiten. Da ich noch sehr gut weiß, wie verwirrend der Einstieg sein kein, habe ich mir die Zeit genommen, dir ein kleines schnelles Grundgerüst für "RezepteBearbeiten" zu bauen. Möglicherweise erkennst du die Vorteile gegenüber deinem momentanen Stil.

09.02.2025 - 16:07 Uhr

In deinem Code ist bisher niemand zuständig:

<TextBlock Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" DockPanel.Dock="Bottom" Padding="5,5,5,1"
                                        Width="{TemplateBinding Width}" TextAlignment="Left" FontWeight="Bold"
                                        Text="{TemplateBinding Content, Converter={StaticResource SplitKonv}, ConverterParameter=1}" />
                            <!-- Resizable Thumb -->
                            <Thumb Grid.Row="1" Grid.Column="2" Width="5" Cursor="SizeWE"
                                   HorizontalAlignment="Right" Background="Transparent"
                                   DragDelta="Thumb_DragDelta"/>
private void Thumb_DragDelta(object sender, DragDeltaEventArgs e)
{
    if (sender is Thumb thumb && thumb.TemplatedParent is GridViewColumnHeader header)
    {
        if (header.Column != null)
        {
            double newWidth = header.Column.Width + e.HorizontalChange;
            header.Column.Width = Math.Max(newWidth, 20); // Mindestbreite setzen
        }
    }
}
16.08.2024 - 04:46 Uhr

Moin,

ich gehe üblicherweise den Weg, die Eingabe über einen zusätzlichen Dialog anzubieten. Möglicherweise ist das auch eine Alternative für dich.

<DataGridTemplateColumn.CellTemplate>
    <DataTemplate>
            <TextBlock Style="{StaticResource FormTextBlockStyle}" Text="{Binding Note}" TextAlignment="Left" HorizontalAlignment="Left"/>
    </DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
    <DataTemplate>
        <DockPanel LastChildFill="True">
            <Button Content="Bearbeiten" Margin="0,0,4,0" Command="{Binding EditNoteCommand}"/>
            <TextBlock HorizontalAlignment="Left" TextAlignment="Left" VerticalAlignment="Center" Style="{StaticResource FormTextBlockStyle}" Text="{Binding Note}" />
        </DockPanel>
    </DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>

Solltest du bei deinem Vorhaben bleiben wollen, musst du dich ein wenig mit den DataGridCell - Events und dem Stichwort "Eingabefokus" beschäftigen.

<Popup
	IsOpen="{Binding IsSelected, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridCell}}}"
    Placement="MousePoint"
    StaysOpen="False">
    <TextBox
        Width="300"
        Height="200"
        AcceptsReturn="True"
        Text="{Binding Note}" />
15.07.2024 - 02:30 Uhr

Das würde mich ebenfalls interessieren, passierte öfter in den letzten Wochen. Lösung bisher war das Löschen der [obj] und [bin] Ordner mit anschließender Projekt Neuerstellung.

16.01.2023 - 17:11 Uhr

Ich verstehe nicht ganz was du vorhast, aber vermutlich dies:


            string name = null;
            string pass =null;


            foreach (DataRow datarow in table.Rows)
            {
                name= datarow["Benutzername"].ToString();
                pass = datarow["Passwort"].ToString();
            
               if (name.Contains(Txt_Benutzername.Text) && pass.Contains(Txt_Passwort.Text))
               {
                   MessageBox.Show("Klappt");
               }
               else
               {
                   MessageBox.Show("Klappt nicht");
               }
            }

26.11.2022 - 04:28 Uhr

Nicht einfach aufgeben 🙂

Die DataGrid Eigenschaft AutoGenerateColumns sollte das erste Problem lösen.


<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding ProjektUmsatzTabelle}"/>

Diese kannst du natürlich auch so setzen:


Dtg_Umsatzvorschau.AutoGenerateColumns=false;
Dtg_Umsatzvorschau.ItemsSource = ProjektUmsatzTabelle;

Formatierung:
Standard numeric format strings

Wenn du das erledigt hast, ist dies eine Variante für das nächste Problem:


<DataGrid.ColumnHeaderStyle>
    <Style TargetType="{x:Type DataGridColumnHeader}">
        <Setter Property="FontWeight"  Value="Bold" />
        <Setter Property="HorizontalAlignment" Value="Center" />
        <Setter Property="HorizontalContentAlignment" Value="Center" />
    </Style>
</DataGrid.ColumnHeaderStyle>

Du solltest neben all dem aber versuchen zu verstehen, warum deine Bindung in XAML nicht klappt, die im Codebehind jedoch schon.

25.11.2022 - 03:53 Uhr

<DataGrid ItemsSource="{Binding Path=TestViewModel.ProjektUmsatzTabelle}"/>

Du bindest damit bereits an die Auflistung deines Typs "Jahrestabelle".
Analog zu Th69´s Vorgehen sollte es dann so in etwa gehen:


...
Spalte = new DataGridTextColumn() { Header = "Kunde", Binding = new Binding("Kunde") };
Spalte = new DataGridTextColumn() { Header = "Projekt", Binding = new Binding("Projekt") };
...
for (int j = ProgStartJahr; j ≤ ProgEndJahr; j++)
    {
        Spalte = new DataGridTextColumn()
        {
            Header = j,
            Binding = new Binding(string.Format("[{0}].Volumen", j - ProgStartJahr))
        };
        Dgd_Umsatzvorschau.Columns.Add(Spalte);
    }

23.11.2022 - 04:41 Uhr

Ich verstehe immer noch nicht ganz wie das aussehen soll, aber mein erster Gedanke ging in diese Richtung (vor meinem eigentlichen Job schnell getippt 🙂


public class JahresTabelle : ObservableCollection<VolumeDetail>
        {
            public JahresTabelle(string kunde, string projectName)
            {
                Kunde = kunde;
                ProjektName = projectName;
            }

            public string Kunde { get; set; }
            public string ProjektName { get; set; }
        }


public class VolumeDetail
        {
            public VolumeDetail(int jahr, double[] volumen)
            {
                Jahr = jahr;
                Volumen = volumen.ToList();

                Summe = Volumen == null ? 0 : Summe = Volumen.Sum();
            }

            public int Jahr
            { get; private set; }

            public List<double> Volumen
            { get; private set; }

            public double Summe
            { get; private set; }
        }


            Random random = new Random();

            var projektBezeichnungsTabelle = new List<JahresTabelle>();
            var progStartJahr = 2022;
            var progEndJahr = 2030;

            //erstellt 50 Kunden mit einem Projekt
            for (int tempID = 0; tempID < 50; tempID++)
            {
                var data = new JahresTabelle("Kunde" + tempID, "Projekt" + tempID);
                for (int jahr = progStartJahr; jahr < progEndJahr; jahr++)
                {
                    var volumesOfYear = new double[]
                    {
                        random.NextDouble(),
                        random.NextDouble(),
                        random.NextDouble(),
                    };                    
                    data.Add(new VolumeDetail(jahr, volumesOfYear));                
                }
                projektBezeichnungsTabelle.Add(data);
            }


            //create columns
            DataGridTextColumn column;

            var columnCollection = new List<DataGridTextColumn>();
            {
                //kunde
                column = new DataGridTextColumn()
                {
                    Header = "Kunde",
                    Binding = new Binding("Kunde")
                };
                columnCollection.Add(column);

                //projekt
                column = new DataGridTextColumn()
                {
                    Header = "ProjektName",
                    Binding = new Binding("ProjektName")
                };
                columnCollection.Add(column);

                //dynamic year columns               
                for (int j = progStartJahr; j < progEndJahr; j++)
                {
                    column = new DataGridTextColumn()
                    {
                        Header = "Vol. im Jahr " + j,
                        Binding = new Binding(string.Format("[{0}].Jahr.Summe", j - progStartJahr))
                    };
                    columnCollection.Add(column);
                }

22.11.2022 - 18:28 Uhr

Mir ist nicht ganz klar, wie das Ergebnis aussehen soll oder darf:

Kunde 1 | Projekt 1 | [44,5] [2,3] [0,8]
Kunde 1 | Projekt 2 | [2,3] [4,5] [9,8] [3,3]
Kunde 2 | Projekt 1 | [9,8] [2,5]

oder eher so?

Kunde 1 | Projekt 1 | 44,5
Kunde 1 | Projekt 1 | 2,3
Kunde 1 | Projekt 1 | 0,8
Kunde 1 | Projekt 2 | 2,3
Kunde 2 | Projekt 2 | 4,5
Kunde 2 | Projekt 2 | 9,8
Kunde 2 | Projekt 2 | 3,3
usw...

21.11.2022 - 18:03 Uhr

Guten Abend,

ich suche nach einem Lösungsansatz. Ich möchte eine Arbeitswoche über 7 Spalten darstellen, welche initial je 10 Zeilen für ein Employee - Drop bereitstellt (siehe Bild im Anhang). Aktuell habe ich mich hier für ein DataGrid als Basis entschieden und versuche dies entsprechend meiner Ideen zu modifizieren. Hierbei suche ich nach für mich attraktiven Varianten ein Modell zu entwickeln, welches "einfach" gebunden und via ViewModel modifiziert werden kann. Mein aktueller Ansatz sieht aktuell so aus, erscheint mir aber unnötig kompliziert. Habt ihr andere Ansätze?


public class MatrixSource : ObservableCollection<MatrixRow>
    {
        public MatrixSource(int year, int week, string workingArea, ShiftEnum shift, int initialRowCount = 10)
        {            
            WeekOfYear = year;
            Week = week;
            WorkingArea = workingArea;
            Shift = shift;
            DateRange = DateProvider.Get_WeekDateRangeOf(week, year);

            Build(DateRange, initialRowCount);
        }
        public List<MatrixColumn> ColumnInfoList
        { get; private set; }

        private void
            Build(CalendarDateRange dateRange, int initialRowCount)
        {
            #region create columninfos

            ColumnInfoList = new List<MatrixColumn>();
            
            var startDate = dateRange.Start.Date;

            while(startDate < dateRange.End.Date.AddDays(1))
            {
                var columnHeader = new ShiftHeaderData(new DateInfo(startDate), ShiftEnum.Unbekannt, string.Empty, string.Empty);
                var columnInfo 
                    = new MatrixColumn(this, ColumnInfoList.Count, columnHeader);

                ColumnInfoList.Add(columnInfo);

                startDate = startDate.AddDays(1);
            }

            #endregion

            #region create default rows

            for (int rowIndex = 0; rowIndex < 10; rowIndex++)
            {
                var row = new MatrixRow(this, rowIndex, ColumnInfoList.Count);
                row.OnRowDataChanged += Row_DataChanged;

                Add(row);
            }                

            #endregion
        }


public class MatrixColumn : BaseNotify
    {
        public MatrixColumn(MatrixSource parent, int displayIndex, ShiftHeaderData headerData)
        {
            ParentMatrix= parent;
            DisplayIndex = displayIndex;
            HeaderData = headerData;
        }

        #region properties

        public MatrixSource ParentMatrix
        { get; private set; }

        public ShiftColumn ParentColumn
        { get; set; }

        public int DisplayIndex 
        { get; private set; }

        public ShiftHeaderData HeaderData 
        { get; private set; }

        #endregion
    }


public class MatrixRow : ObservableCollection<EmployeeCellData> , INotifyPropertyChanged
    {
        public MatrixRow(MatrixSource parentMatrix, int displayIndex, int defaultCellCount = 7)
        {
            ParentMatrix = parentMatrix;
            DisplayIndex = displayIndex;

            for (int cellIndex = 0; cellIndex < defaultCellCount; cellIndex++)
            {
                var cell = new EmployeeCellData(parentMatrix, this);

                cell.OnDataChanged += Cell_OnDataChanged;

                Add(cell);
            }
        }
   }

In XAML binde ich so:


<local:ShiftDataGrid x:Name="DebugDG" Grid.Column="1" Grid.Row="1" MinRowHeight="30"                                 
                                 GenerateDefaultMatrixSource="True"
                                 MatrixSource="{Binding Path=MatrixSource, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">

Damit mein DataGrid damit auch was anfangen kann:


public class ShiftDataGrid : DataGrid, INotifyPropertyChanged
    {
        public ShiftDataGrid()
        {
            InitMe();
        }

        #region events

        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        #endregion

        #region properties
       
        public static readonly DependencyProperty MatrixSourceProperty = DependencyProperty.Register("MatrixSource", typeof(MatrixSource), typeof(ShiftDataGrid), new PropertyMetadata(OnMatrixSourceChanged));
        public MatrixSource MatrixSource
        {
            get { return (MatrixSource)GetValue(MatrixSourceProperty); }
            set
            {
                SetValue(MatrixSourceProperty, value);
            }
        }

        public static readonly DependencyProperty GenerateDefaultMatrixSourceProperty = DependencyProperty.Register("GenerateDefaultMatrixSource", typeof(bool), typeof(ShiftDataGrid), new PropertyMetadata(false));
        public bool GenerateDefaultMatrixSource
        {
            get { return (bool)GetValue(GenerateDefaultMatrixSourceProperty); }
            set
            {
                SetValue(GenerateDefaultMatrixSourceProperty, value);
            }
        }
        private void
            Build_Matrix(MatrixSource matrixData)
        {
            IsBusy = true;
            {
                SelectedCells?.Clear();
                SelectedLastEmployeeCell = null;
                Columns?.Clear();

                if (matrixData != null)
                {
                    foreach (var columnInfo in matrixData.ColumnInfoList)
                    {
                        columnInfo.ParentColumn 
                            = new ShiftColumn(columnInfo.HeaderData);

                        Columns.Add(columnInfo.ParentColumn);
                    }

                    ItemsSource = matrixData;
                }
            }
            IsBusy = false;
        }

        private static void
            OnMatrixSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            (sender as ShiftDataGrid).Build_Matrix(e.NewValue as MatrixSource);
        }


Und eine entsprechende DataGridColumn:


public class ShiftColumn : DataGridBoundColumn
    {
        public ShiftColumn(ShiftHeaderData headerData)
        {
            this.Header = headerData;
        }
       protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
        {
            EmployeeCellPresenter presenter = new EmployeeCellPresenter() //<----- UserControl als Drag&Drop target/sourc
            {                
                IsDragEnabled = true,
                IsDragIndicatorEnabled = true,
                IsDropEnabled = true,
                IsDropIndicatorEnabled = true
            };

            Binding binding;

            #region bind DataContext

            binding = new Binding();
            {
                binding.Source = cell;
                binding.Mode = BindingMode.TwoWay;
                binding.Path = new PropertyPath("DataContext");
            }
            BindingOperations.SetBinding(presenter, EmployeeCellPresenter.DataContextProperty, binding);

            #endregion

            #region bind cell data

            binding = new Binding();
            {
                binding.Source = cell;
                binding.Mode = BindingMode.TwoWay;
                binding.Path = new PropertyPath("DataContext.[" + cell.Column.DisplayIndex + "]");
            }
            BindingOperations.SetBinding(presenter, EmployeeCellPresenter.CellDataProperty, binding);

            #endregion            
            ...     


public class EmployeeCellData : BaseNotify
    {
        public EmployeeModel Data { get; set; }
        ...
    }

18.08.2022 - 14:43 Uhr

Aus deinem switch (bool) solltest du ein if () else machen.
Und ich würde die View erst erzeugen und dann zuweisen:

@Abt

  1. Das hatte ich bereits als Verbesserung angenommen.

@Th69
2. Das wollte ich wissen und wirkt unter deinem Beispiel dann auch gut erklärt.


Aber irgendwie wußte ich wohl intuitiv, daß etwas an der View-Erzeugung nicht stimmt...

Darum hab ich mich an euch gewendet. Danke nochmal.

18.08.2022 - 05:59 Uhr

Ok, das war mir auch noch nicht bekannt.

The problem there is that CollectionViewSource.GetDefaultView(object) will always return the same ICollectionView instance for a given source, and this is what any ItemsControl extension will use when displaying that source.

Diese Änderung brachte den Erfolg:


//var view = CollectionViewSource.GetDefaultView(uc.Source);
var view = new CollectionViewSource { Source = uc.Source }.View;

Danke für eure Unterstützung.

18.08.2022 - 05:23 Uhr

Guten Morgen,

Deine Hinweise habe ich umgesetzt, obwohl ich beim 2ten nicht
wüsste warum das sinnvoller ist !?

Aus deinem switch (bool) solltest du ein if () else machen.
Und ich würde die View erst erzeugen und dann zuweisen:

Zu meinem Kernproblem bleibt der Aha Effekt noch immer aus.

  • Source aller 3 Views sind aktiv gebunden
  • Zum Start der Anwendung stehen bereits 3 - 4 Meldungen an.
  • Filter wie folgt aktiviert
  • Es greift nur der Filter in der DebugView

Der Reihe nach aktiviert:
View Teamleiter: Application, Distributor, Setup
View Debug: Application, Distributor, Setup


USER        @FilterFunction:  Filter: 0
TEAMLEITER  @FilterFunction:  Filter: 0
DEBUG       @FilterFunction:  Filter: 0
DEBUG       @FilterFunction:  Filter: 0
DEBUG       @FilterFunction:  Filter: 0
TEAMLEITER  @FilterChanges    Filter: Application
TEAMLEITER  @CommandExecute   Filter: Application
DEBUG       @FilterFunction:  Filter: 0
DEBUG       @FilterFunction:  Filter: 0
DEBUG       @FilterFunction:  Filter: 0
TEAMLEITER  @FilterChanges    Filter: Application, Distributor
TEAMLEITER  @CommandExecute   Filter: Application, Distributor
DEBUG       @FilterFunction:  Filter: 0
DEBUG       @FilterFunction:  Filter: 0
DEBUG       @FilterFunction:  Filter: 0
TEAMLEITER  @FilterChanges    Filter: Application, Distributor, Setup
TEAMLEITER  @CommandExecute   Filter: Application, Distributor, Setup
DEBUG       @FilterFunction:  Filter: 0
DEBUG       @FilterFunction:  Filter: 0
DEBUG       @FilterFunction:  Filter: 0
DEBUG       @FilterChanges    Filter: Application
DEBUG       @CommandExecute   Filter: Application
DEBUG       @FilterFunction:  Filter: Application
DEBUG       @FilterFunction:  Filter: Application
DEBUG       @FilterFunction:  Filter: Application
DEBUG       @FilterChanges    Filter: Application, Distributor
DEBUG       @CommandExecute   Filter: Application, Distributor
DEBUG       @FilterFunction:  Filter: Application, Distributor
DEBUG       @FilterFunction:  Filter: Application, Distributor
DEBUG       @FilterFunction:  Filter: Application, Distributor
DEBUG       @FilterChanges    Filter: Application, Distributor, Setup
DEBUG       @CommandExecute   Filter: Application, Distributor, Setup
DEBUG       @FilterFunction:  Filter: Application, Distributor, Setup
DEBUG       @FilterFunction:  Filter: Application, Distributor, Setup
DEBUG       @FilterFunction:  Filter: Application, Distributor, Setup

17.08.2022 - 17:18 Uhr

OK, Danke Th69. Das war dann wohl der entscheidende Hinweis.


<local:UC_Message Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                          Title="MELDUNGEN"
                          Source="{Binding Path=Data.MessageViewModel.Messages, Source={StaticResource MainViewModel}, UpdateSourceTrigger=PropertyChanged}"
                          Margin="5,0,5,5"/>

Dies hatte ich wie gesagt in insgesamt mehreren SubViews so integriert. Nachdem ich nun alle
Source - Bindings bis auf einen auskommentiert habe, funktionierts. Ich weiß nur noch
nicht warum... Weil die zugrundeliegende Source - Auflistung im MessageViewModel die selbe ist?

17.08.2022 - 17:02 Uhr

Da UC_Message selbst die View ist und ich diese wie üblich instanziiere, glaube ich das gleiche Objekt zu nutzen, ja.


<local:UC_Message Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                          Title="MELDUNGEN"
                          Source="{Binding Path=Data.MessageViewModel.Messages, Source={StaticResource MainViewModel}, UpdateSourceTrigger=PropertyChanged}"
                          Margin="5,0,5,5"/>

17.08.2022 - 16:29 Uhr

Evtl. noch interessant. Zeigt immer korrekt die gerade
gesetzten Filter an.


<TextBlock DockPanel.Dock="Right" Foreground="{StaticResource MyBlue}" Margin="20,0"
 Text="{Binding Filter, ElementName=ThisUC,FallbackValue=noFilter, StringFormat={}Aktive Filter: {0}}" VerticalAlignment="Center"/>

17.08.2022 - 16:14 Uhr

Hier erfolgt nur die Zuweisung des Filter Predikats:


/* set view */
            switch (uc.Source == null)
            {
                case true:
                    uc.View = null; break;

                default:
                    {
                        uc.View = CollectionViewSource.GetDefaultView(uc.Source);
                        uc.View.Filter = uc.Filter_Function; //<------------------------------------------
                        break;
                    }
            }

Über ein DataProvider werden die Enumerationswerte in eine Liste geholt, in einem Menu
dargestellt, angeklickt und dadurch FilterItemClickCommand aufgerufen.


<ObjectDataProvider MethodName="GetValues" ObjectType="{x:Type sys:Enum}" x:Key="MessageTypeProvider">
            <ObjectDataProvider.MethodParameters>
                <x:Type TypeName="helper:MessageTypeEnum" />
            </ObjectDataProvider.MethodParameters>
        </ObjectDataProvider>

<Button Content="Filter anpassen"...
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding Source={StaticResource MessageTypeProvider}}">
                            <ContextMenu.ItemContainerStyle>
                                <Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource MahApps.Styles.MenuItem}">
                                    <Setter Property="StaysOpenOnClick" Value="True"/>
                                    <Setter Property="Foreground" Value="{StaticResource MyBlue}"/>
                                    <Setter Property="Header" Value="{Binding Converter={StaticResource GetEnumDescriptionConverter}}"/>
                                    <Setter Property="IsCheckable" Value="true"/>
                                    <Setter Property="Command" Value="{Binding Data.FilterItemClickCommand, Source={StaticResource ThisUCProxy}}"/>
                                    <Setter Property="CommandParameter" Value="{Binding}"/>
                                </Style>
                            </ContextMenu.ItemContainerStyle>
                        </ContextMenu>
...
...

Filter_Function dürfte nur hier gezielt durch Refresh aufgerufen werden!?


_FilterItemClickCommand = new DelegateCommand<object>((s) =>
            {
                var _typeOfMessage = (MessageTypeEnum)s;
                                
                if (Filter.HasFlag(_typeOfMessage))
                {
                    Filter &= ~_typeOfMessage; // remove it
                }
                else
                {
                    Filter |= _typeOfMessage;  // add it
                }

                Debug.WriteLine("active filter @CommandExecute: " + Filter);
                View?.Refresh(); //<---------------------------------------------------------
            },
            (s) => { return true; });

Das MessageModel selbst sieht so aus:


public class Base_MessageModel
    {
        public Base_MessageModel(MessageTypeEnum _target, string _message)
        {
            Target = _target;
            RawMessage = _message;
            Date = DateTime.Now;
        }

        public Base_MessageModel(MessageTypeEnum _target, EndPoint _clientEndPoint, EndPoint _serverEndPoint, string _message)
        {
            Target = _target;
            RawMessage = _message;
            ClientEndPoint = _clientEndPoint;
            ServerEndPoint = _serverEndPoint;

            Date = DateTime.Now;
        }

        public MessageTypeEnum Target { get; private set; }

        private string _RawMessage;
        public string RawMessage
        {
            get { return _RawMessage; }
            set
            {
                _RawMessage = value;
                Message = Get_PrintableMessage(value);
            }
        }
        public string Message { get; private set; }
...
...

17.08.2022 - 14:42 Uhr

Das kann ich soweit akzeptieren, hilft mir aber leider nicht beim Problem.

17.08.2022 - 11:01 Uhr

Hallo,

mir konkreter Titel ist mir nicht eingefallen.

Zu meinem Problem:
Mein MessageViewModel stellt eine Auflistung IEnumerable<Base_MessageModel> bereit.
Die MessageView dazu wird an mehreren Stellen im Programm verwendet und erstellt
aus der o.g. Quelle immer eine CollectionView:


<DataGrid ItemsSource="{Binding Path=View, ElementName=ThisUC, UpdateSourceTrigger=PropertyChanged}">


public partial class UC_Message : UserControl
    {
        public UC_Message()
        {
            InitializeComponent();

            _FilterItemClickCommand = new DelegateCommand<object>((s) =>
            {
                var _typeOfMessage = (MessageTypeEnum)s;

                switch (Filter.HasFlag(_typeOfMessage) == false)
                {
                    case true:
                        Filter |= _typeOfMessage; break;

                    default:
                        Filter &= ~_typeOfMessage; break;
                }
                                
                Debug.WriteLine("active filter @CommandExecute: " + Filter);
                View?.Refresh();
            },
            (s) => { return true; });
        }


public static readonly DependencyProperty ViewProperty = DependencyProperty.Register("View", typeof(ICollectionView), typeof(UC_Message));
        public ICollectionView View
        {
            get { return (ICollectionView)GetValue(ViewProperty); }
            set
            {
                SetValue(ViewProperty, value);
            }
        }
public static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(IEnumerable<Base_MessageModel>), typeof(UC_Message), new PropertyMetadata(OnSourceChanged));
        public IEnumerable<Base_MessageModel> Source
        {
            get { return (IEnumerable<Base_MessageModel>)GetValue(SourceProperty); }
            set
            {
                SetValue(SourceProperty, value);
            }
        }

private static void
            OnSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var uc = (UC_Message)sender;

            /* set view */
            switch (uc.Source == null)
            {
                case true:
                    uc.View = null; break;

                default:
                    {
                        uc.View = CollectionViewSource.GetDefaultView(uc.Source);
                        uc.View.Filter = uc.Filter_Function;
                        break;
                    }
            }
        }

Ziel ist, dass jede Instanz der View in der Lage ist die gleiche Datenbasis bei Bedarf unterschiedlich zu filtern. Dazu hier noch etwas um dies zu können:


public static readonly DependencyProperty Filter_IsEnabledProperty = DependencyProperty.Register("Filter_IsEnabled", typeof(bool), typeof(UC_Message), new PropertyMetadata(true));
        public bool Filter_IsEnabled
        {
            get { return (bool)GetValue(Filter_IsEnabledProperty); }
            set
            {
                SetValue(Filter_IsEnabledProperty, value);
            }
        }

        public static readonly DependencyProperty FilterProperty = DependencyProperty.Register("Filter", typeof(MessageTypeEnum), typeof(UC_Message), new PropertyMetadata(Filter_Changed));
        public MessageTypeEnum Filter
        {
            get { return (MessageTypeEnum)GetValue(FilterProperty); }
            set
            {
                SetValue(FilterProperty, value);
            }
        }

private static void
            Filter_Changed(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var uc = (UC_Message)sender;

            Debug.WriteLine("active filter @FilterChanged: " + uc.Filter); // <---------------- nur zum prüfen was hier los ist
        }

Die Filter Funktion sieht so aus und vermutlich liegt hier das Problem. Debug.WriteLine gibt
hier stets active filter @FilterFunction: 0 zurück was ich mir beim besten Willen einfach
nicht mehr erklären kann.


private bool
            Filter_Function(object messageObject)
        {
            if (Filter_IsEnabled == false)
                return true;

            var message = messageObject as Base_MessageModel;

            Debug.WriteLine("active filter @FilterFunction: " + Filter); //<------------------------ warum ist Filter hier immer (int)0

            return Filter.HasFlag(message.Target);
        }

Die Enumeration dazu sieht wie folgt aus:


[Flags]
    public enum MessageTypeEnum
    {
        //[Description("Kein Filter")]
        //None = 0,

        [Description("Anwendung")]
        Application = 1,

        [Description("Distributor")]
        Distributor = 2,

        [Description("Setup")]
        Setup = 4,

        [Description("Debugger")]
        Debug = 8,

        [Description("Baumer Simulator")]
        BaumerSimulator = 16,

        [Description("Wolke Simulator")]
        WolkeSimulator = 32,        
    }

Einer dieser Enumerationwerte wird dann ausschließlich hier ergänzt / entfernt:


_FilterItemClickCommand = new DelegateCommand<object>((s) =>
            {
                var _typeOfMessage = (MessageTypeEnum)s;

                switch (Filter.HasFlag(_typeOfMessage) == false)
                {
                    case true:
                        Filter |= _typeOfMessage; break;

                    default:
                        Filter &= ~_typeOfMessage; break;
                }
                                
                Debug.WriteLine("active filter @CommandExecute: " + Filter);
                View?.Refresh();
            },
            (s) => { return true; });
        }

Schalte ich nun nacheinander die Filter "Application, Distributor, Setup und Debug" an, sieht die Ausgabe so aus:
[Debug.WriteLine Ausgabe]


active filter @FilterFunction: 0
active filter @FilterChanged: Application
active filter @CommandExecute: Application
active filter @FilterFunction: 0
active filter @FilterChanged: Application, Distributor
active filter @CommandExecute: Application, Distributor
active filter @FilterFunction: 0
active filter @FilterChanged: Application, Distributor, Setup
active filter @CommandExecute: Application, Distributor, Setup
active filter @FilterFunction: 0
active filter @FilterChanged: Application, Distributor, Setup, Debug
active filter @CommandExecute: Application, Distributor, Setup, Debug

Frage: Warum ist die "Filter" - Property in der Funktion "Filter_Function" immer 0 und macht den Filter damit wirkungslos ?

26.08.2021 - 12:20 Uhr

Habs, Danke dir.

25.08.2021 - 18:52 Uhr

Hallo,

ich nutze im WPF DataGrid das SourceUpdated Event um Änderungen
am Benutzer (PMUser) zu validieren:


<DataGrid...
  Style="{Controls:StaticResource MahApps.Styles.DataGrid.Azure}"
  SelectedItem="{Binding SelectedItem}"
  ItemsSource="{Binding Path=PM_UserList, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}">

<i:Interaction.Triggers>                 
 <i:EventTrigger EventName="SourceUpdated">
       <i:InvokeCommandAction Command="{Binding DataSourceUpdatedCommand}" PassEventArgsToCommand="True"/>
  </i:EventTrigger>
 </i:Interaction.Triggers>

<DataGrid.Columns>
 <DataGridTextColumn Header="Name" Binding="{Binding Path=Username, Mode=TwoWay, UpdateSourceTrigger=LostFocus, NotifyOnSourceUpdated=True}" Width="100"/>
 <DataGridComboBoxColumn Header="Level" ItemsSource="{Binding Source={StaticResource UserRoleProvider}}" SelectedItemBinding="..."/>
 <DataGridTextColumn Header="HasValidationError" Binding="{Binding HasInvalidData, Mode=OneWay}"/>

PMUser implementiert hier INotifyPropertyChanged wie folgt:


public class MySQLNotifier
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public event PropertyChangedEventHandler SQLChangedDataPropertyChanged;

        public void OnPropertyChanged(string prop)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
        }

        public void OnSQLDataPropertyChanged(string prop)
        {
            if (IsInitialized)
                HasChanged = true;

            SQLChangedDataPropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
        }
    }


public class PMUser : MySQLNotifier, IPMValidation
    {
        private ObservableCollection<App_ErrorEnum> _ValidationErrors = new ObservableCollection<App_ErrorEnum>();
        public ObservableCollection<App_ErrorEnum> ValidationErrors
        {
            get { return _ValidationErrors; }
            set
            {
                _ValidationErrors = value;
                OnPropertyChanged(nameof(ValidationErrors));
            }
        }

        private bool _HasInvalidData = false;
        public bool HasInvalidData
        {
            get { return _HasInvalidData; }
            set
            {
                _HasInvalidData = value;
                OnPropertyChanged("HasInvalidData");
            }
        }

        private int _ValidationErrorCount = 0;
        public int ValidationErrorCount
        {
            get { return _ValidationErrorCount; }
            set
            {
                _ValidationErrorCount = value;                
                OnPropertyChanged("ValidationErrorCount");
            }
        }

        private string _Username;
        public string Username
        {
            get { return _Username; }
            set
            {
                _Username = value;
                OnSQLDataPropertyChanged("Username");
                OnSQLDataPropertyChanged("IsCurrentUser");
            }
        }

        private UserRoleEnum _Userlevel;
        public UserRoleEnum Userlevel
        {
            get { return _Userlevel; }
            set
            {
                _Userlevel = value;
                OnSQLDataPropertyChanged("Userlevel");
            }
        }
        ...
        public void
            Validation_Add(App_ErrorEnum newError)
        {
            ValidationErrors.Add(newError);
        }

        public void
            Validate()
        {
            /* clear old validation */
            ValidationErrors.Clear();
            
            /* check empty username */
            if (string.IsNullOrEmpty(Username))
                Validation_Add(App_ErrorEnum.EmptyUsername);
            else
            {
                PMUser _found = SQLConnector.Get_PMUser(Username);
                if (_found != null)
                {
                    /* new user: username exist */
                    /* old user: new username exist */
                    if (IsNew || SQL_ID != _found.SQL_ID)
                        Validation_Add(App_ErrorEnum.UsernameAlreadyExist);
                }
            }
            ...
            HasInvalidData = ValidationErrors.Count > 0;
            ValidationErrorCount = ValidationErrors.Count;

            ValidationString = string.Empty;
            foreach (var item in ValidationErrors)
                ValidationString += item + " ";
       }

PMUser.Validate() wird nun über das erwähnte SourceUpdatedEvent im UserViewModel ausgeführt:


private void On_DataSourceChanged(DataTransferEventArgs args)
{
            var changedUser = ((FrameworkElement)args.OriginalSource).DataContext as PMUser;
            if (changedUser == null)
                return;

            /* validate user data */
            changedUser.Validate();

            [breakPoint]
}

Über den BreakPoint erkenne ich, dass die Validierung erfolgt ist sobald bspw. username = string.Empty:
[changedUser ValidationErrorCount = 1, ValidationErrors = Count = 1, HasInvalidData = true, ValidationString = "EmptyUsername "]

Das Problem:
Im DataGrid werden diese Columns nicht aktualisiert (zeigen 0, bzw. false), obwohl das Model die korrekten Werte enthält.


<DataGridTextColumn Header="HasValidationError" Binding="{Binding HasInvalidData, Mode=OneWay}"/>
<DataGridTextColumn Header="ValidationErrorCount" Binding="{Binding ValidationErrorCount, Mode=OneWay}"/>

Diese DEBUG-Column zeigt hierbei immer den korrekten Zähler an Validation - Errors.


<DataGridTextColumn Header="ECount" Binding="{Binding ValidationErrors.Count, Mode=OneWay}"/>

Was übersehe ich?