Я пишу свой первый проект в WPF и не могу осмыслить остальную проблему.
У меня есть DataGrid, который использует ItemSource из таблицы DataSet (локальная база данных в XML). Пользователь должен иметь возможность добавлять столбцы в DataSet / DataGrid и устанавливать столбец DataTemplate, например текст, изображение, дату, ...
Поэтому я должен использовать один DataTemplate для нескольких столбцов и изменить путь привязки на основе имени столбца, например:
<DataTemplate x:Key="ImageColumnTemplate">
<Grid>
<Image Source="{Binding Path=CURRENT_COLUMN_NAME Converter={StaticResource ImageReader}}" />
<TextBox Text="{Binding Path=CURRENT_COLUMN_NAME}"/>
</Grid>
</DataTemplate>
Я понимаю, что это неправильный подход, но мне не удалось найти решение, которое:
-Не основан на сериализации / клонировании XAML - не работает, потому что теряются родительские ссылки.
-Возможность записывать значение в строку в отличие от "Path =." используя унаследованный DataGridBoundColumn вместо DataGridTemplateColumn.
DataGridTextColumn делает это каким-то образом, и он работает:
Dim fGridCol = New DataGridTextColumn() With {.Header = fColumn.ColumnName}
fGridCol.Binding = New Binding(fColumn.ColumnName) With {.Mode = BindingMode.TwoWay}
Но DataGridTemplateColumn не имеет привязки, а DataGridBoundColumn не записывает значение, если оно унаследовано.
Как вы можете заставить эту работу работать?
ИЗМЕНИТЬ
Позвольте мне поставить свой вопрос в ином контексте:
Лучшее, что у меня есть на данный момент:
<Window x:Class="MainWindow"
...
<Window.Resources>
<local:CellStringReader x:Key="StringReader" />
<local:CellImageReader x:Key="ImageReader" />
<Style x:Key="TextBlockToggle" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridCell}}, Path=IsEditing}" Value="True">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="TextBoxToggle" TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGridCell}, Path=IsEditing}" Value="False">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
<DataTemplate x:Key="ImageColumnTemplate">
<Grid Focusable="True">
<Grid HorizontalAlignment="Left" Background="Transparent">
<Button PreviewMouseDown="SelectImageFile" >
<Image x:Name="ImageTemplateImage" Height="20" Width="20"
Source="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridCell}}, UpdateSourceTrigger=PropertyChanged , Converter={StaticResource ImageReader}}"/>
</Button>
</Grid>
<TextBlock x:Name="ImageTemplateTextBlock" Margin="25,0,0,0"
Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridCell}}, UpdateSourceTrigger=PropertyChanged , Converter={StaticResource StringReader}}"/>
<TextBox x:Name="ImageTemplateTextBox" Margin="23,0,0,0" BorderThickness="0" Style="{StaticResource TextBoxToggle}"
Text="{Binding Mode=TwoWay, Path=., RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridCell}}, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource StringReader}}"/>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid>
...
<DataGrid x:Name="LocalGrid" Grid.Row="1" AutoGenerateColumns="False" CanUserAddRows="False">
<DataGrid.RowValidationRules>
<local:RowDataValidationRule/>
</DataGrid.RowValidationRules>
</DataGrid>
...
</Grid>
</Window>
И
Class MainWindow
Protected Overrides Sub OnInitialized(e As EventArgs)
LocalGrid.ItemsSource = Base.Tables("Local").DefaultView
CreateColumns()
End Sub
Private WithEvents Base As New Base
Private WithEvents LocalTable As DataView = Base.Tables("Local").DefaultView
Private Sub CreateColumns()
Dim LocalTable = Base.Tables("Local")
Dim TypesTable = Base.Tables("ColumnTypes")
For Each fColumn As DataColumn In LocalTable.Columns
Dim ColumnType As String = (From fRow As DataRowView In TypesTable.DefaultView Where fRow.Item("Name") = String.Format("Local." & fColumn.ColumnName) Select fRow.Item("Template") Take 1).FirstOrDefault()
If ColumnType = "Image" Then 'THIS IS IMAGE COLUMN
Dim ImageColumn As New DataGridTemplateColumn With {.Header = fColumn.ColumnName}
ImageColumn.CellTemplate = Me.FindResource("ImageColumnTemplate")
ImageColumn.CellEditingTemplate = Me.FindResource("ImageColumnTemplate")
LocalGrid.Columns.Add(ImageColumn)
Else 'THIS IS REGILAR COLUMN
Dim fGridCol = New DataGridTextColumn() With {.Header = fColumn.ColumnName}
fGridCol.Binding = New Binding(fColumn.ColumnName) With {.Mode = BindingMode.TwoWay, .UpdateSourceTrigger = UpdateSourceTrigger.LostFocus}
LocalGrid.Columns.Add(fGridCol)
End If
Next
End Sub
Private Sub SelectImageFile(ByVal sender As Object, ByVal e As RoutedEventArgs)
'This creates OpenFileDialog on button click
End Sub
End Class
Public Class CellStringReader : Implements IValueConverter
Private EditingCell As DataGridCell
Public Overridable Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert
Dim Cell As DataGridCell = value
Dim Row As DataRowView = Cell.DataContext
Dim Column As DataGridColumn = Cell.Column
If Cell.IsEditing Then
EditingCell = Cell
Else
EditingCell = Nothing
End If
Return Row.Item(Column.Header)
End Function
Public Overridable Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
If EditingCell Is Nothing Then 'This is not callded, ever.
Throw New Exception("No cell editing")
End If
Return EditingCell
End Function
End Class
Public Class CellImageReader : Inherits CellStringReader
Public Overrides Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object
value = MyBase.Convert(value, targetType, parameter, culture)
If IsDBNull(value) OrElse String.IsNullOrWhiteSpace(value) Then
Return Nothing
ElseIf IO.File.Exists(value) Then
Return New BitmapImage(New Uri(value))
End If
End Function
Public Overrides Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object
Throw New NotSupportedException
End Function
End Class
Проблема в том, что редактирование TextBox в сгенерированном столбце Image не вызывает CellStringReader.ConvertBack () и не записывает измененное значение базового DataRow.
Я так понимаю, это потому, что "Путь =." в TextBox Binding, но я не знаю альтернатив.
При синтаксическом анализе XAML в строке Button PreviewMouseDown прерывается из-за отсутствия контекста, и он все равно не записывает значение.
Мой вопрос в том, как заставить TextBox записывать новое значение в DataRow.?
Надеюсь, теперь будет больше сеансов, и извините за длинный пост.