補充範例及優化程式碼,進一步將XAML上關於欄位(DataGridTextColumn)的描述收攏到C#端的程式碼內

f417de2dfb1aaaa041680fe5ecab55b5.jpg

//將log加入AppLog動態資料集(_appLogCollection),顯示在DataGrid上
void AppendLog(LogLevel level, string msg, [CallerLineNumber] int lineNumber = 0, [CallerMemberName] string memberName = "")
{
    AppLog log = new AppLog()
    {
        Level = level.Name.ToUpper(),
        ThreadID = Thread.CurrentThread.ManagedThreadId,
        Message = msg,
        CallerLineNumber = lineNumber,
        CallerMemberName = memberName,
    };

    _appLogCollection.Add(log);
}

另外,資料結構內有實作OnPropertyChanged/OnPropertiesChanged的欄位,當資料有變更時,UI顯示上也會跟著變更

程式碼端優化:

XAML優化前:
<TabItem Header="AppLog">
    <Grid Background="#FFE5E5E5">
        <DataGrid x:Name="DataGridAppLog" Margin="0" AutoGenerateColumns="False" CanUserAddRows="False" SelectionMode="Extended" SelectionUnit="Cell" IsSynchronizedWithCurrentItem="True">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding CreatedTime}"/>
                <DataGridTextColumn Binding="{Binding Level}"/>
                <DataGridTextColumn Binding="{Binding ThreadID}"/>
                <DataGridTextColumn Binding="{Binding CallerLineNumber}"/>
                <DataGridTextColumn Binding="{Binding CallerMemberName}"/>
                <DataGridTextColumn Binding="{Binding Message}"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</TabItem>

XAML優化後:
<TabItem Header="AppLog">
    <Grid Background="#FFE5E5E5">
        <DataGrid x:Name="DataGridAppLog" Margin="0" AutoGenerateColumns="False" CanUserAddRows="False" SelectionMode="Extended" SelectionUnit="Cell" IsSynchronizedWithCurrentItem="True"/>
    </Grid>
</TabItem>

C#優化前:
//根據欄位特性(ColumnAttribute),為DataGrid的欄位(DataGridTextColumn)建立初始設定
static void SetHeadersByBindings<T>(this DataGrid obj, IDictionary<string, T> propertyNameMap) where T : ColumnAttribute
{
    foreach (DataGridColumn column in obj.Columns)
    {
        if (column is DataGridBoundColumn bound && bound.Binding is Binding bind)
        {
            if (propertyNameMap.TryGetValue(bind.Path.Path, out T attr))
            {
                column.Header = attr.WPFName;
                column.DisplayIndex = attr.WPFDisplayIndex;
                bind.StringFormat = attr.WPFStringFormat;
                column.IsReadOnly = attr.WPFIsReadOnly;
                column.Visibility = attr.WPFVisibility.ToString().ConvertTo<Visibility>();
                column.CanUserReorder = attr.WPFCanUserReorder;
                column.CanUserSort = attr.WPFCanUserSort;

                //https://stackoverflow.com/questions/4577944/how-to-resize-wpf-datagrid-to-fit-its-content
                column.Width = new DataGridLength(1.0, DataGridLengthUnitType.Auto);

                Style headerS = new Style(typeof(DataGridColumnHeader));
                headerS.Setters.Add(new Setter(ToolTipService.ToolTipProperty, $"{column.DisplayIndex},{attr.CSVName},{bind.Path.Path},{bind.StringFormat}"));
                column.HeaderStyle = headerS;

                //https://stackoverflow.com/questions/53961533/datagrid-columns-element-style-in-codebehind-has-no-effect
                if (column is DataGridTextColumn col)
                {
                    Style elementS = null;

                    if (attr.WPFHorizontalAlignment != WPFHorizontalAlignment.Left)
                    {
                        elementS = new Style();
                        elementS.Setters.Add(new Setter(FrameworkElement.HorizontalAlignmentProperty, attr.WPFHorizontalAlignment.ToString().ConvertTo<HorizontalAlignment>()));
                    }

                    if (!string.IsNullOrWhiteSpace(attr.WPFForeground))
                    {
                        if (elementS == null)
                        {
                            elementS = new Style();
                        }

                        elementS.Setters.Add(new Setter(TextBlock.FontWeightProperty, FontWeights.DemiBold));
                        elementS.Setters.Add(new Setter(TextBlock.ForegroundProperty, new BrushConverter().ConvertFromString(attr.WPFForeground)));
                    }

                    if (elementS != null)
                    {
                        col.ElementStyle = elementS;
                    }
                }
            }
        }
    }
}

C#優化後(SetHeadersByBindings更名為SetColumns):
private static void SetColumn<T>(this DataGridBoundColumn obj, Binding bind, T attr) where T : ColumnAttribute
{
    obj.Header = attr.WPFName;
    obj.DisplayIndex = attr.WPFDisplayIndex;
    bind.StringFormat = attr.WPFStringFormat;
    obj.IsReadOnly = attr.WPFIsReadOnly;
    obj.Visibility = attr.WPFVisibility.ToString().ConvertTo<Visibility>();
    obj.CanUserReorder = attr.WPFCanUserReorder;
    obj.CanUserSort = attr.WPFCanUserSort;

    //https://stackoverflow.com/questions/4577944/how-to-resize-wpf-datagrid-to-fit-its-content
    obj.Width = new DataGridLength(1.0, DataGridLengthUnitType.Auto);

    //https://stackoverflow.com/questions/4615081/how-to-add-a-tooltip-for-a-datagrid-header-where-the-header-text-is-generated-d
    Style headerS = new Style(typeof(DataGridColumnHeader));
    headerS.Setters.Add(new Setter(ToolTipService.ToolTipProperty, $"{obj.DisplayIndex},{attr.CSVName},{bind.Path.Path},{bind.StringFormat}"));
    obj.HeaderStyle = headerS;

    //https://stackoverflow.com/questions/53961533/datagrid-columns-element-style-in-codebehind-has-no-effect
    if (obj is DataGridTextColumn col)
    {
        Style elementS = null;

        if (attr.WPFHorizontalAlignment != WPFHorizontalAlignment.Left)
        {
            elementS = new Style();
            elementS.Setters.Add(new Setter(FrameworkElement.HorizontalAlignmentProperty, attr.WPFHorizontalAlignment.ToString().ConvertTo<HorizontalAlignment>()));
        }

        if (!string.IsNullOrWhiteSpace(attr.WPFForeground))
        {
            if (elementS == null)
            {
                elementS = new Style();
            }

            elementS.Setters.Add(new Setter(TextBlock.FontWeightProperty, FontWeights.DemiBold));
            elementS.Setters.Add(new Setter(TextBlock.ForegroundProperty, new BrushConverter().ConvertFromString(attr.WPFForeground)));
        }

        if (elementS != null)
        {
            col.ElementStyle = elementS;
        }
    }
}

public static void SetColumn<T>(this DataGridBoundColumn obj, T attr) where T : ColumnAttribute
{
    if (obj.Binding is Binding bind)
    {
        obj.SetColumn(bind, attr);
    }
}

public static void SetColumns<T>(this DataGrid obj, IDictionary<string, T> propertyNameMap) where T : ColumnAttribute
{
    HashSet<string> existedBindings = new HashSet<string>();

    foreach (DataGridColumn column in obj.Columns)
    {
        if (column is DataGridBoundColumn bound && bound.Binding is Binding bind && propertyNameMap.ContainsKey(bind.Path.Path))
        {
            existedBindings.Add(bind.Path.Path);
        }
    }

    foreach (KeyValuePair<string, T> pair in propertyNameMap)
    {
        if (pair.Value.WPFDisplayIndex < 0 || existedBindings.Contains(pair.Key))
        {
            continue;
        }

        //https://stackoverflow.com/questions/6885711/wpf-c-bind-datagrid-column-with-code-behind
        DataGridTextColumn column = new DataGridTextColumn()
        {
            Binding = new Binding(pair.Key),
        };

        obj.Columns.Add(column);
    }

    foreach (DataGridColumn column in obj.Columns)
    {
        if (column is DataGridBoundColumn bound && bound.Binding is Binding bind && propertyNameMap.TryGetValue(bind.Path.Path, out T attr))
        {
            bound.SetColumn(bind, attr);
        }
    }
}

創作者介紹
創作者 GNAySolution 的頭像
Yang

GNAySolution

Yang 發表在 痞客邦 留言(0) 人氣( 1 )