none
WPF - Problem mit DataGrid in ContextMenü bzw. mit DataGrid allgemein RRS feed

  • Frage

  • Hallo zusammen,

    vorab möchte ich sagen, dass ich bisher wenig Erfahrung mit WPF habe und diese ganzen Dinge wie Bindings etc. noch lernen muss. Aber ich habe ein Problem mit einem DataGrid. Wahrscheinlich ist das relativ einfach für Kenner. Zur Situation:

    Ich möchte in einm Context Menü ein DataGrid darstellen. In der 1. Spalte soll die Bezeichnung (Label) und in der 2. Spalte eine Textbox zur Eingabe enthalten sein.

    Ich habe es über folgenden Weg versucht:

    <MenuItem StaysOpenOnClick="True">
    		<MenuItem.Header>
    						<DataGrid Name="dg_TimerSettings" CanUserAddRows="False" CanUserDeleteRows="False" CanUserResizeRows="False" CanUserSortColumns="False" CanUserReorderColumns="False" CanUserResizeColumns="False" HeadersVisibility="None">
    										<DataGrid.Columns>
    														<DataGridTextColumn></DataGridTextColumn>
    														<DataGridTextColumn></DataGridTextColumn>
    										</DataGrid.Columns>
    										<DataGridRow></DataGridRow>
    										<DataGridRow></DataGridRow>
    										<DataGridRow></DataGridRow>
    										<DataGridCell Width="100" Height="25" Grid.Row="0" Grid.Column="0" Content="Hours:" Name="lbl_TimerSettingsHours"></DataGridCell>
    										<DataGridCell Width="100" Height="25" Grid.Row="0" Grid.Column="1" Content="00" Name="txt_TimerSettingsHours"></DataGridCell>
    						</DataGrid>
    		</MenuItem.Header>
    </MenuItem>

    Mit diesem Code erhalte ich allerdings folgenden Fehler.

    System.ArgumentNullException

    Value cannot be null.Parameter name: key


       at System.Collections.Generic.Dictionary`2.FindEntry(TKey key)   at System.Collections.Generic.Dictionary`2.TryGetValue(TKey key, TValue& value)   at System.Windows.Controls.DataGridItemAttachedStorage.TryGetValue(Object item, DependencyProperty property, Object& value)   at System.Windows.Controls.DataGridRow.RestoreAttachedItemValue(DependencyObject objectWithProperty, DependencyProperty property)   at System.Windows.Controls.DataGridRow.SyncProperties(Boolean forcePrepareCells)   at System.Windows.Controls.DataGridRow.PrepareRow(Object item, DataGrid owningDataGrid)   at System.Windows.Controls.DataGrid.PrepareContainerForItemOverride(DependencyObject element, Object item)   at System.Windows.Controls.ItemsControl.MS.Internal.Controls.IGeneratorHost.PrepareItemContainer(DependencyObject container, Object item)   at System.Windows.Controls.ItemContainerGenerator.System.Windows.Controls.Primitives.IItemContainerGenerator.PrepareItemContainer(DependencyObject container)   at System.Windows.Controls.VirtualizingStackPanel.InsertContainer(Int32 childIndex, UIElement container, Boolean isRecycled)   at System.Windows.Controls.VirtualizingStackPanel.AddContainerFromGenerator(Int32 childIndex, UIElement child, Boolean newlyRealized)   at System.Windows.Controls.VirtualizingStackPanel.MeasureOverride(Size constraint)   at System.Windows.Controls.Primitives.DataGridRowsPresenter.MeasureOverride(Size constraint)   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)   at System.Windows.UIElement.Measure(Size availableSize)   at MS.Internal.Helper.MeasureElementWithSingleChild(UIElement element, Size constraint)   at System.Windows.Controls.ItemsPresenter.MeasureOverride(Size constraint)   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)   at System.Windows.UIElement.Measure(Size availableSize)   at MS.Internal.Helper.MeasureElementWithSingleChild(UIElement element, Size constraint)   at System.Windows.Controls.ScrollContentPresenter.MeasureOverride(Size constraint)   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)   at System.Windows.UIElement.Measure(Size availableSize)   at System.Windows.Controls.Grid.MeasureCell(Int32 cell, Boolean forceInfinityV)   at System.Windows.Controls.Grid.MeasureCellsGroup(Int32 cellsHead, Size referenceSize, Boolean ignoreDesiredSizeU, Boolean forceInfinityV)   at System.Windows.Controls.Grid.MeasureOverride(Size constraint)   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)   at System.Windows.UIElement.Measure(Size availableSize)   at System.Windows.Controls.ScrollViewer.MeasureOverride(Size constraint)   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)   at System.Windows.UIElement.Measure(Size availableSize)   at System.Windows.Controls.Border.MeasureOverride(Size constraint)   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)   at System.Windows.UIElement.Measure(Size availableSize)   at System.Windows.Controls.Control.MeasureOverride(Size constraint)   at System.Windows.Controls.DataGrid.MeasureOverride(Size availableSize)   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)   at System.Windows.UIElement.Measure(Size availableSize)   at System.Windows.Media.VisualBrush.DoLayout(UIElement element)   at System.Windows.Media.VisualBrush.LayoutCallback(Object arg)   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)

    Ich verstehe nicht, warum die DataGrid Füllung so kompliziert ist im Gegensatz zu WinForms. Wenn mir einer von Euch einen Tipp geben könnte wäre ich echt dankbar.

    Viele Grüße

    Dennis Nxxxxx

    Mittwoch, 8. Mai 2013 09:15

Antworten

  • Hallo zusammen,

    danke für eure Hilfe. Ich hatte zwischenzeitlich bereits eine andere Lösung gefunden.

    Das DataGrid benötigte ich lediglich, um zwei Controls nebeneinander darzustellen. Nun weiß ich aber, dass es einfacher geht. :-)

    Hier meine neue Lösung:

    <MenuItem StaysOpenOnClick="True" FontFamily="pack://Uhr:,,,/Resources/#AgencyFB" FontWeight="Bold" Name="MenuItemTimer" Header="Timer" FontSize="15" IsEnabled="True" Foreground="Black">
    	<MenuItem Name="MenuItemTimerSettingsHours" StaysOpenOnClick="True" >
    					<MenuItem.Header>
    									<Grid>
    													<Grid.Resources>
    													</Grid.Resources>
    													<Grid.ColumnDefinitions>
    																	<ColumnDefinition />
    																	<ColumnDefinition />
    													</Grid.ColumnDefinitions>
    													<Grid.RowDefinitions>
    																	<RowDefinition />
    																	<RowDefinition />
    																	<RowDefinition />
    																	<RowDefinition />
    													</Grid.RowDefinitions>
    													<Label Content="Hours: " Name="lbl_TimerSettingsHours" Grid.Row="0" Grid.Column="0" />
    													<StackPanel Orientation="Horizontal" Grid.Row="0" Grid.Column="1">
    																	<TextBox TextChanged="txt_NumHours_TextChanged" x:Name="txt_NumHours" x:FieldModifier="private" Margin="5,5,0,5" Width="30" Text="0" />
    																	<Button FontFamily="pack://Uhr:,,,/Resources/#AgencyFB" Click="btn_UpHours_Click" x:Name="btn_UpHours" x:FieldModifier="private" Margin="5,5,0,5" Content="^" Height="18" Width="18" />
    																	<Button FontFamily="pack://Uhr:,,,/Resources/#AgencyFB" Click="btn_DownHours_Click" x:Name="btn_DownHours" x:FieldModifier="private" Margin="0,5,0,5"  Content="?" Height="18" Width="18" />
    													</StackPanel>
    													<Label Content="Minutes: " Name="lbl_TimerSettingsMinutes" Grid.Row="1" Grid.Column="0" />
    													<StackPanel Orientation="Horizontal" Grid.Row="1" Grid.Column="1">
    																	<TextBox TextChanged="txt_NumMinutes_TextChanged" x:Name="txt_NumMinutes" x:FieldModifier="private" Margin="5,5,0,5" Width="30" Text="0" />
    																	<Button FontFamily="pack://Uhr:,,,/Resources/#AgencyFB" Click="btn_UpMinutes_Click" x:Name="btn_UpMinutes" x:FieldModifier="private" Margin="5,5,0,5" Content="^" Height="18" Width="18" />
    																	<Button FontFamily="pack://Uhr:,,,/Resources/#AgencyFB" Click="btn_DownMinutes_Click" x:Name="btn_DownMinutes" x:FieldModifier="private" Margin="0,5,0,5"  Content="?" Height="18" Width="18" />
    													</StackPanel>
    													<Label Content="Seconds: " Name="lbl_TimerSettingsSeconds" Grid.Row="2" Grid.Column="0" />
    													<StackPanel Orientation="Horizontal" Grid.Row="2" Grid.Column="1">
    																	<TextBox TextChanged="txt_NumSeconds_TextChanged" x:Name="txt_NumSeconds" x:FieldModifier="private" Margin="5,5,0,5" Width="30" Text="0" />
    																	<Button FontFamily="pack://Uhr:,,,/Resources/#AgencyFB" Click="btn_UpSeconds_Click" x:Name="btn_UpSeconds" x:FieldModifier="private" Margin="5,5,0,5" Content="^" Height="18" Width="18" />
    																	<Button FontFamily="pack://Uhr:,,,/Resources/#AgencyFB" Click="btn_DownSeconds_Click" x:Name="btn_DownSeconds" x:FieldModifier="private" Margin="0,5,0,5"  Content="?" Height="18" Width="18" />
    													</StackPanel>
    													<Label Content="Timer Action: " Name="lbl_TimerSettingsAction" Grid.Row="3" Grid.Column="0" />
    													<StackPanel Orientation="Horizontal" Grid.Row="3" Grid.Column="1">
    																	<ComboBox FontFamily="pack://Uhr:,,,/Resources/#AgencyFB" Height="23" Name="cbo_TimerSettingsAction">
    																					<ComboBoxItem Name="cbo_ItemBeep" IsSelected="True" Content="Alarm (Beep)" />
    																					<ComboBoxItem Name="cbo_ItemSleep" Content="Sleep Computer" />
    																					<ComboBoxItem Name="cbo_ItemShutdown" Content="Shutdown Computer" />
    																	</ComboBox>
    													</StackPanel>
    									</Grid>
    					</MenuItem.Header>
    	</MenuItem>
    	<MenuItem>
    					<MenuItem.Header>
    									<Button FontFamily="pack://Uhr:,,,/Resources/#AgencyFB" Name="btn_SetTimer" Click="btn_SetTimer_Click">Start Timer</Button>
    					</MenuItem.Header>
    	</MenuItem>
    </MenuItem>
    </MenuItem>

    Viele Grüße

    Dennis Nxxxxx

    PS: Wieso ist es unüblich ein DataGrid oder sonstige Controls in einem Context Menü zu integrieren. Für meine Anwendung passt das prima!

    Montag, 13. Mai 2013 07:17

Alle Antworten

  • Hallo,

    vorab: ich halte das ContextMenu dafür für sehr sehr ungeeignet, aufgrund seiner Menü-Typischen Eigenschaften und des Layouts.

    Zum Problem:
    Eigentlich ist es in WPF ziehmlich einfach Controls zu befüllen, egal welche. Man muss nur vorher alles genau verstanden haben.

    Du brauchst einen eigenen Datentyp für die Inhalte des DataGrid's. Ich finde es auf folgender Seite recht gut beschrieben:
    http://www.c-sharpcorner.com/UploadFile/mahesh/datagrid-in-wpf/

    Wenn du die Auflistung der Elemente direkt in die Klasse legst, dann reagiert das DataGrid auch auf änderungen in der Liste. Außerdem kannst du so geänderte Daten besser abfragen.

    Eine Alternative zum ContextMenu:
    Fange das MouseUp-Event deines Controls ab und zeige ggf. ein Popup an:
    http://www.biggle.de/blog/das-wpf-control-popup-howto

    Bei diesem kannst du das Verhalten und das Layout ganz individuel bestimmen.


    <Code:13/> - Koopakiller [kuːpakɪllɐ]
    Webseite | Code Beispiele | Facebook | Snippets
    Wenn die Frage beantwortet ist, dann markiert die hilfreichsten Beiträge als Antwort und bewertet die Beiträge. Danke.
    Einen Konverter zwischen C# und VB.NET Code gibt es hier.

    Mittwoch, 8. Mai 2013 15:29
    Moderator
  • Hi Dennis,
    wenn Du dir mal die Fehlerursache anschaust, dann siehst Du, dass der Fehler wegen unzureichender Information zur Größenberechnung entsteht (...PopupRoot.MeasureOverride(Size...)). Unklar ist, warum Du im Header eines MenuItems ein Grid anwenden musst.
     
    --
    Peter Fleischer
    Mittwoch, 8. Mai 2013 16:35
  •  

    Hallo Dennis,

    was du da vorhast ist zwar reichlich ungewöhnlich aber gehen tut so etwas, es gäbe sogar mehrere Möglichkeiten eine davon wäre:

    <Menu Grid.Row="0">
                <MenuItem Header="Menu...">
                    <MenuItem Header="(none)"/>
                    <Separator/>
    
                    <MenuItem>
                        <MenuItem.Template>
                            <ControlTemplate>
                                <ItemsControl ItemsSource="{Binding Items}">
                                    <ItemsControl.ItemTemplate>
                                        <DataTemplate>
                                            <DataGrid Height="150">
                                                <DataGrid.Columns>
                                                    <DataGridTextColumn Header="Column1"></DataGridTextColumn>
                                                    <DataGridTextColumn Header="Column2"></DataGridTextColumn>
                                                    <DataGridTextColumn Header="Column3"></DataGridTextColumn>
                                                    <DataGridTextColumn Header="Column4"></DataGridTextColumn>
                                                </DataGrid.Columns>
                                            </DataGrid>
                                        </DataTemplate>
                                    </ItemsControl.ItemTemplate>
                                </ItemsControl>
                            </ControlTemplate>
                        </MenuItem.Template>
                    </MenuItem>
                </MenuItem>
            </Menu>

    damit wäre das DataGrid direkt im MenuItem.

    Und wenn du das unbedingt im ContextMenu haben möchtest dann geht das u.a. über einen Style:

    <Style TargetType="{x:Type ContextMenu}">
                <Setter Property="SnapsToDevicePixels" Value="True"/>
                <Setter Property="OverridesDefaultStyle" Value="True"/>
                <Setter Property="Grid.IsSharedSizeScope" Value="true"/>
                <Setter Property="HasDropShadow" Value="True"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ContextMenu}">
                            <Border 
              Name="Border"
              Background="{StaticResource WindowBackgroundBrush}"
              BorderBrush="{StaticResource SolidBorderBrush}"
              BorderThickness="1" >
                                <DataGrid Height="150">
                                    <DataGrid.Columns>
                                        <DataGridTextColumn Header="Column1" />
                                        <DataGridTextColumn Header="Column2" />
                                        <DataGridTextColumn Header="Column3" />
                                        <DataGridTextColumn Header="Column4" />
                                    </DataGrid.Columns>
                                </DataGrid>
                            </Border>
                            <ControlTemplate.Triggers>
                                <Trigger Property="HasDropShadow" Value="true">
                                    <Setter TargetName="Border" Property="Padding" Value="0,3,0,3"/>
                                    <Setter TargetName="Border" Property="CornerRadius" Value="4"/>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>

    und hier noch das Menu:

    <Menu Grid.Row="0">
                <MenuItem Header="Menu...">
                    <MenuItem.ContextMenu>
                        <ContextMenu Name="ctx"></ContextMenu>
                    </MenuItem.ContextMenu>
                </MenuItem>
            </Menu>

    Aber wie schon erwähnt ist das völlig ungewöhnlich, mal abgesehen davon wegen 2 Spalten ein DataGrid zu nehmen... na ja, du wirst schon wissen was du tust *smile*... ach ja, das was ich dir hier gezeigt habe sind nur Beispiele, man kann das auch auf völlig andere Weise implementieren.

    I hope that hepls ;-)

    Gruß Carl


    Carl-Christian Schaffert Bahnhofsweg 2 82008 Unterhaching mobile: 0152-33 72 79 49 email: carl-christian.schaffert@dotnet-dev-expert.de skype: carl.christian.schaffert


    Mittwoch, 8. Mai 2013 19:13
  • Hallo zusammen,

    danke für eure Hilfe. Ich hatte zwischenzeitlich bereits eine andere Lösung gefunden.

    Das DataGrid benötigte ich lediglich, um zwei Controls nebeneinander darzustellen. Nun weiß ich aber, dass es einfacher geht. :-)

    Hier meine neue Lösung:

    <MenuItem StaysOpenOnClick="True" FontFamily="pack://Uhr:,,,/Resources/#AgencyFB" FontWeight="Bold" Name="MenuItemTimer" Header="Timer" FontSize="15" IsEnabled="True" Foreground="Black">
    	<MenuItem Name="MenuItemTimerSettingsHours" StaysOpenOnClick="True" >
    					<MenuItem.Header>
    									<Grid>
    													<Grid.Resources>
    													</Grid.Resources>
    													<Grid.ColumnDefinitions>
    																	<ColumnDefinition />
    																	<ColumnDefinition />
    													</Grid.ColumnDefinitions>
    													<Grid.RowDefinitions>
    																	<RowDefinition />
    																	<RowDefinition />
    																	<RowDefinition />
    																	<RowDefinition />
    													</Grid.RowDefinitions>
    													<Label Content="Hours: " Name="lbl_TimerSettingsHours" Grid.Row="0" Grid.Column="0" />
    													<StackPanel Orientation="Horizontal" Grid.Row="0" Grid.Column="1">
    																	<TextBox TextChanged="txt_NumHours_TextChanged" x:Name="txt_NumHours" x:FieldModifier="private" Margin="5,5,0,5" Width="30" Text="0" />
    																	<Button FontFamily="pack://Uhr:,,,/Resources/#AgencyFB" Click="btn_UpHours_Click" x:Name="btn_UpHours" x:FieldModifier="private" Margin="5,5,0,5" Content="^" Height="18" Width="18" />
    																	<Button FontFamily="pack://Uhr:,,,/Resources/#AgencyFB" Click="btn_DownHours_Click" x:Name="btn_DownHours" x:FieldModifier="private" Margin="0,5,0,5"  Content="?" Height="18" Width="18" />
    													</StackPanel>
    													<Label Content="Minutes: " Name="lbl_TimerSettingsMinutes" Grid.Row="1" Grid.Column="0" />
    													<StackPanel Orientation="Horizontal" Grid.Row="1" Grid.Column="1">
    																	<TextBox TextChanged="txt_NumMinutes_TextChanged" x:Name="txt_NumMinutes" x:FieldModifier="private" Margin="5,5,0,5" Width="30" Text="0" />
    																	<Button FontFamily="pack://Uhr:,,,/Resources/#AgencyFB" Click="btn_UpMinutes_Click" x:Name="btn_UpMinutes" x:FieldModifier="private" Margin="5,5,0,5" Content="^" Height="18" Width="18" />
    																	<Button FontFamily="pack://Uhr:,,,/Resources/#AgencyFB" Click="btn_DownMinutes_Click" x:Name="btn_DownMinutes" x:FieldModifier="private" Margin="0,5,0,5"  Content="?" Height="18" Width="18" />
    													</StackPanel>
    													<Label Content="Seconds: " Name="lbl_TimerSettingsSeconds" Grid.Row="2" Grid.Column="0" />
    													<StackPanel Orientation="Horizontal" Grid.Row="2" Grid.Column="1">
    																	<TextBox TextChanged="txt_NumSeconds_TextChanged" x:Name="txt_NumSeconds" x:FieldModifier="private" Margin="5,5,0,5" Width="30" Text="0" />
    																	<Button FontFamily="pack://Uhr:,,,/Resources/#AgencyFB" Click="btn_UpSeconds_Click" x:Name="btn_UpSeconds" x:FieldModifier="private" Margin="5,5,0,5" Content="^" Height="18" Width="18" />
    																	<Button FontFamily="pack://Uhr:,,,/Resources/#AgencyFB" Click="btn_DownSeconds_Click" x:Name="btn_DownSeconds" x:FieldModifier="private" Margin="0,5,0,5"  Content="?" Height="18" Width="18" />
    													</StackPanel>
    													<Label Content="Timer Action: " Name="lbl_TimerSettingsAction" Grid.Row="3" Grid.Column="0" />
    													<StackPanel Orientation="Horizontal" Grid.Row="3" Grid.Column="1">
    																	<ComboBox FontFamily="pack://Uhr:,,,/Resources/#AgencyFB" Height="23" Name="cbo_TimerSettingsAction">
    																					<ComboBoxItem Name="cbo_ItemBeep" IsSelected="True" Content="Alarm (Beep)" />
    																					<ComboBoxItem Name="cbo_ItemSleep" Content="Sleep Computer" />
    																					<ComboBoxItem Name="cbo_ItemShutdown" Content="Shutdown Computer" />
    																	</ComboBox>
    													</StackPanel>
    									</Grid>
    					</MenuItem.Header>
    	</MenuItem>
    	<MenuItem>
    					<MenuItem.Header>
    									<Button FontFamily="pack://Uhr:,,,/Resources/#AgencyFB" Name="btn_SetTimer" Click="btn_SetTimer_Click">Start Timer</Button>
    					</MenuItem.Header>
    	</MenuItem>
    </MenuItem>
    </MenuItem>

    Viele Grüße

    Dennis Nxxxxx

    PS: Wieso ist es unüblich ein DataGrid oder sonstige Controls in einem Context Menü zu integrieren. Für meine Anwendung passt das prima!

    Montag, 13. Mai 2013 07:17