I tried to increment a value as long as I press a button. The incremented value is connected via binding to a TextBox. The problem occures when I update the underlaying integer. I get an exception that another Thread owns it.
<Button Name="Up"
Content="Up"
PreviewMouseLeftButtonDown="Up_PreviewMouseLeftButtonDown"
PreviewMouseLeftButtonUp="Up_PreviewMouseLeftButtonUp">
</Button>
<TextBox Text="{Binding NumericField}">
During Initialisation:
Timer = New Timers.Timer
Timer.Interval = 100
AddHandler Timer.Elapsed, AddressOf Timer_Elapsed
Code behind:
Private _numericField As Integer
Public Property NumericField As Integer
Get
Return _numericField
End Get
Set(ByVal value As Integer)
_numericField = value
RaiseEvent PropertyChanged(Me, New ComponentModel.PropertyChangedEventArgs("NumericField"))
End Set
End Property
Private Sub Timer_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs)
NumericField += 1
End Sub
Private Sub Up_PreviewMouseLeftButtonDown(ByVal sender As System.Object, ByVal e As System.Windows.Input.MouseButtonEventArgs)
Timer.Start()
End Sub
Private Sub Up_PreviewMouseLeftButtonUp(ByVal sender As System.Object, ByVal e As System.Windows.Input.MouseButtonEventArgs)
Timer.Stop()
End Sub
This looks a bit too complicated. Can't you use a RepeatButton?
Simple solution:
<RepeatButton Click="RepeatButton_Click" Content="Up" />
Code behind:
Private Sub RepeatButton_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
NumericField += 1
End Sub
Using commands
Try to avoid unnecessary code in your code behind file. Here's a sketch using Commands
XAML:
<RepeatButton Command="{Binding IncrementField}" Content="Up"></RepeatButton>
Helping class. Re-usable for all commands inside your project:
Public Class ActionCommand
Implements ICommand
Private ReadOnly _executeHandler As Action(Of Object)
Private ReadOnly _canExecuteHandler As Func(Of Object, Boolean)
Public Sub New(ByVal execute As Action(Of Object),
ByVal canExecute As Func(Of Object, Boolean))
If execute Is Nothing Then
Throw New ArgumentNullException("Execute cannot be null")
End If
_executeHandler = execute
_canExecuteHandler = canExecute
End Sub
Public Custom Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged
AddHandler(ByVal value As EventHandler)
AddHandler CommandManager.RequerySuggested, value
End AddHandler
RemoveHandler(ByVal value As EventHandler)
RemoveHandler CommandManager.RequerySuggested, value
End RemoveHandler
RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
CommandManager.InvalidateRequerySuggested()
End RaiseEvent
End Event
Public Sub Execute(ByVal parameter As Object) Implements ICommand.Execute
_executeHandler(parameter)
End Sub
Public Function CanExecute(ByVal parameter As Object) As Boolean Implements ICommand.CanExecute
If (_canExecuteHandler Is Nothing) Then
Return True
End If
Return _canExecuteHandler(parameter)
End Function
End Class
And in your model:
_incrementField = New ActionCommand(AddressOf IncrementExecuted, AddressOf IncrementCanExecute)
...
Private Function IncrementCanExecute(ByVal parameter As Object) As Boolean
Return True
End Function
Private Sub IncrementExecuted(ByVal parameter As Object)
NumericField += 1
End Sub
Private _incrementField As ActionCommand
Public ReadOnly Property IncrementField As ActionCommand
Get
Return _incrementField
End Get
End Property
Related
I am trying to clear session cookies in a WPF WebBrowser. This method
Public Const INTERNET_OPTION_END_BROWSER_SESSION As Integer = 42
<DllImport("wininet.dll", SetLastError:=True)>
Public Function InternetSetOption(ByVal hInternet As IntPtr, ByVal dwOption As Integer, ByVal lpBuffer As IntPtr, ByVal lpdwBufferLength As Integer) As Boolean
End Function
described here How to clear System.Windows.Forms.WebBrowser session data? works for our WinForms applications, but not for WPF.
Any ideas?
Thanks
Turns out the answer is to ensure that the WebBrowser object is created each time the page is loaded.
As the WebBrowser object is not considered to be a control I created a ScrollViewer at initialisation then added the WebBrowser to the ScrollViewer on the loaded event.
It now works perfectly
for Faber75
Imports System.ComponentModel
Class Website_Login_Page
Private LoginBrowser As WebBrowser
Private Sub Website_Login_Page_Initialized(sender As Object, e As System.EventArgs) Handles Me.Initialized
Try
Website_Login_Grid.Background = New SolidColorBrush(Colors.White)
For i As Integer = 0 To 1
Dim vRow As New RowDefinition
If i = 0 Then
vRow.Height = New GridLength(35, GridUnitType.Star)
Else
vRow.Height = New GridLength(35)
End If
Website_Login_Grid.RowDefinitions.Add(vRow)
Next
Dim SV As New ScrollViewer
With SV
.VerticalScrollBarVisibility = ScrollBarVisibility.Auto
.Name = "Website_Login_SV"
End With
RegisterControl(Website_Login_Grid, SV)
'Cookies are not removed if the WebBrowser object is not created each time the page is loaded!
' LoginBrowser = New WebBrowser
Grid.SetRow(SV, 0)
Website_Login_Grid.Children.Add(SV)
Dim DP As DockPanel = PageStatusBarDP(Website_Login_Grid)
Grid.SetRow(DP, 1)
Website_Login_Grid.Children.Add(DP)
Catch ex As Exception
EmailError(ex)
End Try
End Sub
Private Sub Website_Login_Page_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles Me.Loaded
Try
If LoginBrowser Is Nothing Then
LoginBrowser = New WebBrowser
End If
Dim SV As ScrollViewer = Website_Login_Grid.FindName("Website_Login_SV")
SV.Content = LoginBrowser
AddHandler LoginBrowser.Navigating, AddressOf WebBrowser_Loading
AddHandler LoginBrowser.LoadCompleted, AddressOf WebBrowser_Loaded
AddHandler LoginBrowser.Navigated, AddressOf WebBrowser_Navigated
If InternetSetOption(IntPtr.Zero, INTERNET_OPTION_END_BROWSER_SESSION, IntPtr.Zero, 0) = False Then
MessageBox.Show("Returned False")
End If
With LoginBrowser
.Navigate(New Uri("https://website.com"))
End With
Catch ex As Exception
EmailError(ex)
End Try
End Sub
Private Sub Website_Login_Page_Unloaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles Me.Unloaded
Try
If Not LoginBrowser Is Nothing Then
RemoveHandler LoginBrowser.Navigating, AddressOf WebBrowser_Loading
RemoveHandler LoginBrowser.LoadCompleted, AddressOf WebBrowser_Loaded
RemoveHandler LoginBrowser.Navigated, AddressOf WebBrowser_Navigated
LoginBrowser = Nothing
End If
GC.GetTotalMemory(True)
Catch ex As Exception
EmailError(ex)
End Try
End Sub
Private Sub WebBrowser_Loading(ByVal sender As Object, ByVal e As NavigatingCancelEventArgs)
PageStatusBarLoading(Website_Login_Grid)
PageStatusBarRightChangeText(Website_Login_Grid, "Loading... Please wait...")
End Sub
Private Sub WebBrowser_Loaded(ByVal sender As Object, ByVal e As NavigationEventArgs)
Dim vDoc As Object = LoginBrowser.Document
Dim vTitle As String = vDoc.Title
PageStatusBarLoaded(Website_Login_Grid, vTitle)
PageStatusBarRightChangeText(Website_Login_Grid, "Loaded")
End Sub
Private Sub WebBrowser_Navigated(ByVal sender As Object, ByVal e As NavigationEventArgs)
Dim vDoc As Object = LoginBrowser.Document
Dim vTitle As String = vDoc.Title
PageStatusBarLoaded(Website_Login_Grid, vTitle)
PageStatusBarRightChangeText(Website_Login_Grid, "Loaded")
End Sub
End Class
I have created a Class which Inherits ICommand
Public Class RelayCommand(Of T)
Implements ICommand
Private ReadOnly m_oExecute As Action(Of T)
Private ReadOnly m_fCanExecute As Predicate(Of T)
Public Sub New(execute As Action(Of T))
Me.New(execute, Nothing)
End Sub
Public Sub New(execute As Action(Of T), canExecute As Predicate(Of T))
If execute Is Nothing Then
Throw New ArgumentNullException("execute")
End If
Me.m_oExecute = execute
Me.m_fCanExecute = canExecute
End Sub
Public Function CanExecute(parameter As T) As Boolean
Return IIf(Me.m_fCanExecute Is Nothing, True, Me.CanExecute(parameter))
End Function
Public Sub Execute(parameter As T)
Me.execute(parameter)
End Sub
Private Function ICommand_CanExecute(parameter As Object) As Boolean Implements ICommand.CanExecute
Return Me.CanExecute(CType(parameter, T))
End Function
Public Custom Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged
AddHandler(value As EventHandler)
AddHandler CommandManager.RequerySuggested, value
End AddHandler
RemoveHandler(value As EventHandler)
RemoveHandler CommandManager.RequerySuggested, value
End RemoveHandler
RaiseEvent(sender As Object, e As EventArgs)
End RaiseEvent
End Event
Private Sub ICommand_Execute(parameter As Object) Implements ICommand.Execute
Me.Execute(CType(parameter, T))
End Sub
End Class
Public Class RelayCommand
Inherits RelayCommand(Of Object)
Public Sub New(execute As Action(Of Object))
MyBase.New(execute)
End Sub
Public Sub New(execute As Action(Of Object), canExecute As Predicate(Of Object))
MyBase.New(execute, canExecute)
End Sub
End Class
In the .xaml file I have wrote this code:
<ComboBox x:Name="cboDatabases" Width="150">
<i:Interaction.Triggers>
<i:EventTrigger EventName="DropDownOpened">
<i:InvokeCommandAction Command="{Binding DataContext.DropDownOpenedCommand,ElementName=cboDatabases}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
And the Command Object in ViewModel
Private m_oDropDownOpenedCommand As RelayCommand
Public ReadOnly Property DropDownOpenedCommand As ICommand
Get
If IsNothing(m_oDropDownOpenedCommand) Then
Me.m_oDropDownOpenedCommand = New RelayCommand(AddressOf Me.DropDownOpened, Function(param) True)
End If
Return Me.m_oDropDownOpenedCommand
End Get
End Property
Private Sub DropDownOpened()
MessageBox.Show("i did it")
End Sub
When I create a new Instance of the Window the code inserts the Property DropDownOpenedCommand as expected. When I click the combo so as to trigger the event then
the code calls many times this Function in the RelayCommand Class
Public Function CanExecute(parameter As T) As Boolean
Return IIf(Me.m_fCanExecute Is Nothing, True, Me.CanExecute(parameter))
End Function
As a result I get a StackOverflowException at that Line. The CanExecute Returns false after the first time that it gets to that line. BUT The parameter field is always nothing. I think that the parameter being nothing is a problem but I can't figure how to pass the right argument.
Any Ideas??
I know that I can handle the event in the xaml.vb but I want it as a Command in the Viewmodel.
In addition to the answer by #Tom Studee, this method might cause you troubles too:
Public Sub Execute(parameter As T)
Me.execute(parameter)
End Sub
Public Function CanExecute(parameter As T) As Boolean
Return IIf(Me.m_fCanExecute Is Nothing, True, Me.CanExecute(parameter))
End Function
If Me.m_fCanExecute is ever not null, the function recurses on itself by evaluating Me.CanExecute() again. That's where the stackoverflowexception is being generated.
edit:
So the first time you come into this method and Me.m_fCanExecute is not null, it again executes Me.CanExecute(parameter), then Me.m_fCanExecute is null again, so it calls Me.CanExecute(), then Me.m_fCanExecute is null again, so it calls Me.CanExecute(), then Me.m_fCanExecute is null again, so it calls Me.CanExecute(), then Me.m_fCanExecute is null again, so it calls Me.CanExecute(), then Me.m_fCanExecute is null again, so it calls Me.CanExecute() ...
and then you get a StackOverFlowException. It's an infinite loop.
I have a UI element which is a listview that contains in it another listview (one vertical and one horizontal).
The Itemsource of the the First listview is an ObservableCollection of Objects, which in turn contains ObservableCollection of a child object. Both have INotifyPropertyChanged interface implemented and call the PropetyChanged Event when usefull.
If I added new child object, I see my second listview update, but when I change a value, I do not see anything change. Is there anything I must do to refresh my UI element in this case ?
Here is my listview
<ListView x:Name="LstInfoEtape" Margin="0,0,0,0" Style="{StaticResource BaseListView}" Grid.Row="2" Grid.Column="1" >
<ListView.ItemTemplate>
<DataTemplate>
<ListView x:Name="LstEtape" ItemsSource="{Binding LstEtapes}" Background="#00000000" SelectionChanged="ListView_SelectionChanged" ScrollViewer.ScrollChanged="ListView_ScrollChanged">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Border Width="82" CornerRadius="4" BorderBrush="{Binding ColorSelected}" BorderThickness="3,3,3,3" Visibility="{Binding EtapeVisible}" Background="{Binding ColorBackground}" MouseDown="Border_MouseDown">
<Grid Width="80" >
<Grid.RowDefinitions>
<RowDefinition Height="19"/>
</Grid.RowDefinitions>
<TextBlock Margin="2,0,2,0" x:Name="TxtDateRappel" TextWrapping="Wrap" Grid.Row="0" Text="{Binding DateRappelTerminaison,StringFormat=yyyy-MM-dd,NotifyOnSourceUpdated=True}" Visibility="{Binding EtapeVisible}" FontSize="12" Foreground="{Binding ColorForeground}" />
</Grid>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The classes I use are those
Public Class SuiviOperationLite
Implements INotifyPropertyChanged
Private _strNom As String
Private _oLstProcessusLite As ObservableCollection(Of ContratProcessusLite) ' Not implemented yet
Private _oLstEtapeLite As ObservableCollection(Of ContratProcessusEtapeLite)
Public Sub New(ByVal strNom As String,
ByVal oLstProcessusLite As ObservableCollection(Of ContratProcessusLite),
ByVal oLstEtapeLite As ObservableCollection(Of ContratProcessusEtapeLite))
Constructor(strNom, oLstProcessusLite, oLstEtapeLite)
End Sub
Private Sub Constructor(ByVal strNom As String,
ByVal oLstProcessusLite As ObservableCollection(Of ContratProcessusLite),
ByVal oLstEtapeLite As ObservableCollection(Of ContratProcessusEtapeLite))
_strNom = strNom
_oLstProcessusLite = oLstProcessusLite
_oLstEtapeLite = oLstEtapeLite
End Sub
Public Property Nom() As String
Get
Return _strNom
End Get
Set(ByVal value As String)
_strNom = value
End Set
End Property
Public Property LstContratProcessus() As ObservableCollection(Of ContratProcessusLite)
Get
Return _oLstProcessusLite
End Get
Set(ByVal value As ObservableCollection(Of ContratProcessusLite))
_oLstProcessusLite = value
End Set
End Property
Public Property LstEtapes() As ObservableCollection(Of ContratProcessusEtapeLite)
Get
Return _oLstEtapeLite
End Get
Set(ByVal value As ObservableCollection(Of ContratProcessusEtapeLite))
_oLstEtapeLite = value
End Set
End Property
Protected Friend Sub NotifyPropertyChanged(ByVal info As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
End Sub
Public Event PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
End Class
Public Class ContratProcessusEtapeLite
Implements INotifyPropertyChanged
Private _lngId As Long
Private _lngIdSequenceTravail As Long
Private _boolSelected As Boolean
Private _strETapeVisible As String
Private _strColorBackGround As String
Private _strColorForeGround As String
Private _dateRappelTerminaison As Date
Private _oParent As DAL.SuiviOperationLite
Public Sub New(ByVal oParent As DAL.SuiviOperationLite,
ByVal lngId As Long,
ByVal lngIdSequenceTravail As Long,
ByVal boolSelected As Boolean,
ByVal strEtapeVisible As String,
ByVal strColorBackGround As String,
ByVal strColorForeGround As String,
ByVal dateRappelTerminaison As Date)
Constructor(oParent,
lngId, _
lngIdSequenceTravail, _
boolSelected, _
strEtapeVisible, _
strColorBackGround, _
strColorForeGround, _
dateRappelTerminaison)
End Sub
Private Sub Constructor(ByVal oParent As DAL.SuiviOperationLite,
ByVal lngId As Long,
ByVal lngIdSequenceTravail As Long,
ByVal boolSelected As Boolean,
ByVal strEtapeVisible As String,
ByVal strColorBackGround As String,
ByVal strColorForeGround As String,
ByVal dateRappelTerminaison As Date)
_oParent = oParent
_lngId = lngId
_lngIdSequenceTravail = lngIdSequenceTravail
_boolSelected = boolSelected
_strETapeVisible = strEtapeVisible
_strColorBackGround = strColorBackGround
_strColorForeGround = strColorForeGround
_dateRappelTerminaison = dateRappelTerminaison
End Sub
Public Property Parent() As DAL.SuiviOperationLite
Get
Return _oParent
End Get
Set(ByVal value As DAL.SuiviOperationLite)
_oParent = value
End Set
End Property
Public Property ID() As Long
Get
Return _lngId
End Get
Set(ByVal value As Long)
_lngId = value
NotifyPropertyChanged("ID")
End Set
End Property
Public Property IdClientContratSequenceTravail() As Long
Get
Return _lngIdSequenceTravail
End Get
Set(ByVal value As Long)
_lngIdSequenceTravail = value
NotifyPropertyChanged("IdSequence")
End Set
End Property
Public Property Selected() As Boolean
Get
Return _boolSelected
End Get
Set(ByVal value As Boolean)
_boolSelected = value
NotifyPropertyChanged("Selected")
End Set
End Property
Public ReadOnly Property ColorSelected As String
Get
If _boolSelected Then
Return "#FF4394DF"
Else
Return "#FF000000"
End If
End Get
End Property
Public Property EtapeVisible() As String
Get
Return _strETapeVisible
End Get
Set(ByVal value As String)
_strETapeVisible = value
NotifyPropertyChanged("Visible")
End Set
End Property
Public Property ColorBackground() As String
Get
Return _strColorBackGround
End Get
Set(ByVal value As String)
_strColorBackGround = value
NotifyPropertyChanged("Background")
End Set
End Property
Public Property ColorForeground() As String
Get
Return _strColorForeGround
End Get
Set(ByVal value As String)
_strColorForeGround = value
NotifyPropertyChanged("Foreground")
End Set
End Property
Public Property DateRappelTerminaison() As Date
Get
Return _dateRappelTerminaison
End Get
Set(ByVal value As Date)
_dateRappelTerminaison = value
NotifyPropertyChanged("DateRappel")
End Set
End Property
Private Sub NotifyPropertyChanged(ByVal info As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
_oParent.NotifyPropertyChanged("Child")
End Sub
Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
End Class
And here are the functions (There are more processing to get the collection of items, but I doubt it's really relevant :
Private Sub LoadList(colSuivi as ObservableCollection(Of SuiviProductionLite)
LstInfoEtape.ItemsSource = colSuivi
End Sub
Private Sub Border_MouseDown(sender As System.Object, e As System.Windows.Input.MouseButtonEventArgs)
Dim oBorder As Border
Dim oEtapeLite As DAL.ContratProcessusEtapeLite
Select Case e.Source.GetType.ToString
Case "System.Windows.Controls.TextBlock"
Dim oTxt As TextBlock = e.Source
Dim oGrid As Grid = oTxt.Parent
_oCurBorderDet = oGrid.Parent
Case "System.Windows.Controls.Border"
Dim oBrd As Border = e.Source
_oCurBorderDet = e.Source
End Select
If Not _oCurBorderDet.DataContext.GetType().FullName.Equals("MS.Internal.NamedObject") Then
oEtapeLite = _oCurBorderDet.DataContext
oEtapeLite.Selected = True
End If
End Sub
When I do some tracing, I know that both PropertyChanged Events are call wheter I click on my UI. If I do a manual "Items.Refresh", I can also see the UI being changed, but I want to remove that manual refresh as it refresh everything, not only the item just modified, which, when facing a lot of data, takes much more time.
I see one problem
Public Property IdClientContratSequenceTravail() As Long
Get
Return _lngIdSequenceTravail
End Get
Set(ByVal value As Long)
_lngIdSequenceTravail = value
NotifyPropertyChanged("IdSequence")
End Set
End Property
Wrong name in Notify
I want, that if a cell/row goes to edit mode, then, if the user attempts to select a different row, it should try to commit that row, if the row is not committed sucessfully, it should decline the selection request and the editing row should remain selected and in edit mode.
Do you have experience with a good helper for it? any good workaround?
NOTE: I've been struggling with this issue for a long time and already got some experience, so please post only working examples, not random thoughts.
The code bellow is includes extension from here (code normalized to fit StackOverflow screen witdh, sorry).
Imports System.ComponentModel
Imports System.Windows.Threading
Namespace Components
Public NotInheritable Class DataGridSelectionChangingBehavior
Public Shared Function GetEnableSelectionChanging(
ByVal element As DataGrid) As Boolean
If element Is Nothing Then Throw New ArgumentNullException("element")
Return element.GetValue(EnableSelectionChangingProperty)
End Function
Public Shared Sub SetEnableSelectionChanging(
ByVal element As DataGrid, ByVal value As Boolean)
If element Is Nothing Then Throw New ArgumentNullException("element")
element.SetValue(EnableSelectionChangingProperty, value)
End Sub
Public Shared ReadOnly EnableSelectionChangingProperty As _
DependencyProperty =
DependencyProperty.RegisterAttached("EnableSelectionChanging",
GetType(Boolean),
GetType(DataGridSelectionChangingBehavior),
New FrameworkPropertyMetadata(False,
New PropertyChangedCallback(
AddressOf EnableSelectionChanging_PropertyChanged)))
Public Shared Sub AddSelectionChangingHandler(
ByVal element As DataGrid, handler As SelectionChangingEventHandler)
If element IsNot Nothing Then _
element.AddHandler(
DataGridSelectionChangingBehavior.SelectionChangingEvent, handler)
End Sub
Public Shared Sub RemoveSelectionChangingHandler(
ByVal element As DataGrid, handler As SelectionChangingEventHandler)
If element IsNot Nothing Then _
element.RemoveHandler(
DataGridSelectionChangingBehavior.SelectionChangingEvent, handler)
End Sub
Public Shared ReadOnly SelectionChangingEvent As RoutedEvent =
EventManager.RegisterRoutedEvent("SelectionChanging",
RoutingStrategy.Bubble,
GetType(SelectionChangingEventHandler),
GetType(DataGridSelectionChangingBehavior))
Private Shared Sub EnableSelectionChanging_PropertyChanged(
ByVal sender As Object, ByVal e As DependencyPropertyChangedEventArgs)
Dim dataGrid = DirectCast(sender, DataGrid)
If CBool(e.NewValue) Then
AddHandler dataGrid.PreparingCellForEdit,
AddressOf DataGrid_PreparingCellForEdit
AddHandler dataGrid.SelectionChanged,
AddressOf DataGrid_SelectionChanged
Else
RemoveHandler dataGrid.PreparingCellForEdit,
AddressOf DataGrid_PreparingCellForEdit
RemoveHandler dataGrid.SelectionChanged,
AddressOf DataGrid_SelectionChanged
RecentColumn.Remove(dataGrid)
End If
End Sub
Private Shared Sub DataGrid_SelectionChanged(
ByVal sender As Object, ByVal e As SelectionChangedEventArgs)
If e.RemovedItems.Count = 0 Then Exit Sub
Dim dataGrid = DirectCast(sender, DataGrid)
Dim removed = e.RemovedItems(0)
Dim row = dataGrid.GetContainerFromItem(Of DataGridRow)(removed)
Dim scea As New SelectionChangingEventArgs(row,
DataGridSelectionChangingBehavior.SelectionChangingEvent, dataGrid)
dataGrid.RaiseEvent(scea)
If scea.Cancel Then
RemoveHandler dataGrid.SelectionChanged,
AddressOf DataGrid_SelectionChanged
Dim operation = dataGrid.Dispatcher.BeginInvoke(
Sub()
dataGrid.SelectedItem = removed
Dim column As DataGridColumn = Nothing
If RecentColumn.TryGetValue(dataGrid, column) Then
Dim cellsPanel =
row.GetVisualDescendant(Of DataGridCellsPanel)().
Children.Cast(Of DataGridCell)()
Dim recentCell =
If(cellsPanel.SingleOrDefault(
Function(cell) cell.Column Is column),
cellsPanel.FirstOrDefault)
If recentCell IsNot Nothing Then
recentCell.IsEditing = True
Keyboard.Focus(recentCell)
End If
End If
End Sub,
DispatcherPriority.ContextIdle)
AddHandler operation.Completed,
Sub(s, ea) AddHandler dataGrid.SelectionChanged,
AddressOf DataGrid_SelectionChanged
End If
End Sub
Private Shared m_RecentColumn As Dictionary(Of DataGrid, DataGridColumn)
Public Shared ReadOnly Property RecentColumn() As Dictionary(Of DataGrid,
DataGridColumn)
Get
If m_RecentColumn Is Nothing Then m_RecentColumn =
New Dictionary(Of DataGrid, DataGridColumn)()
Return m_RecentColumn
End Get
End Property
Private Shared Sub DataGrid_PreparingCellForEdit(
ByVal sender As Object, e As DataGridPreparingCellForEditEventArgs)
Dim dataGrid = DirectCast(sender, DataGrid)
RecentColumn(dataGrid) = e.Column
End Sub
End Class
Public Delegate Sub SelectionChangingEventHandler(
ByVal sender As Object, ByVal e As SelectionChangingEventArgs)
Public Class SelectionChangingEventArgs : Inherits RoutedEventArgs
Public Sub New(ByVal row As DataGridRow, routedEvent As RoutedEvent)
MyBase.New(routedEvent)
m_CurrentRow = row
End Sub
Public Sub New(ByVal row As DataGridRow,
ByVal routedEvent As RoutedEvent,
ByVal source As Object)
MyBase.New(routedEvent, source)
m_CurrentRow = row
End Sub
Private m_CurrentRow As DataGridRow
Public ReadOnly Property CurrentRow() As DataGridRow
Get
Return m_CurrentRow
End Get
End Property
Public ReadOnly Property Item() As Object
Get
If CurrentRow IsNot Nothing Then Return CurrentRow.Item
Return Nothing
End Get
End Property
Public Property Cancel As Boolean
End Class
End Namespace
Usage:
<DataGrid Name="dg"
src:DataGridSelectionChangingBehavior.EnableSelectionChanging="True"
src:DataGridSelectionChangingBehavior.SelectionChanging="dg_SelectionChanging">
Code behind (pseudu):
Private Sub dg_SelectionChanging(ByVal sender As Object,
ByVal e As SelectionChangingEventArgs)
If e.CurrentRow.IsEditing Then
Dim item = TryCast(e.CurrentRow.Item, MyEntityType)
If item IsNot Nothing AndAlso item.IsValid Then
Dim dataGrid = DirectCast(sender, DataGrid)
Dim committed = dataGrid.CommitEdit(DataGridEditingUnit.Row, True)
If committed Then Exit Sub
End If
e.Cancel = True
End If
End Sub
Note: visual studio might not show the event in the intellisense and might even generate a design-time error, but it should compile and work perfect.
FYI: code formatted modified to fit SO screen.
I'm writing a custom textblock control thats populate hyperlinks and raises event when clicked to hyperlink.
I wrote this code but I got stucked.
My code is :
Imports System.Text.RegularExpressions
Public Class CustomTextBlock
Inherits TextBlock
Public Event Klik As EventHandler(Of EventArgs)
Public ReadOnly InlineCollectionProperty As DependencyProperty = DependencyProperty.Register("InlineCollection", GetType(String), GetType(CustomTextBlock), New PropertyMetadata(New PropertyChangedCallback(AddressOf CustomTextBlock.InlineChanged)))
Private Shared Sub InlineChanged(ByVal sender As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
DirectCast(sender, CustomTextBlock).Inlines.Clear()
Dim kelimeler = Split(e.NewValue, " ")
For i = 0 To kelimeler.Length - 1
If Regex.Match(kelimeler(i), "(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,#?^=%&:/~\+#]*[\w\-\#?^=%&/~\+#])?").Success Then
Dim x = New Hyperlink(New Run(kelimeler(i)))
x.AddHandler(Hyperlink.ClickEvent, New RoutedEventHandler(AddressOf t_Click))
x.ToolTip = kelimeler(i)
x.Tag = kelimeler(i)
DirectCast(sender, CustomTextBlock).Inlines.Add(x)
If Not i = kelimeler.Length Then DirectCast(sender, CustomTextBlock).Inlines.Add(" ")
Else
DirectCast(sender, CustomTextBlock).Inlines.Add(kelimeler(i))
If Not i = kelimeler.Length Then DirectCast(sender, CustomTextBlock).Inlines.Add(" ")
End If
''//Console.WriteLine(kelime(i).ToString.StartsWith("#"))
Next
kelimeler = Nothing
End Sub
Public Property InlineCollection As String
Get
Return DirectCast(GetValue(InlineCollectionProperty), String)
End Get
Set(ByVal value As String)
SetValue(InlineCollectionProperty, value)
End Set
End Property
Private Shared Sub t_Click(ByVal sender As Hyperlink, ByVal e As System.Windows.RoutedEventArgs)
e.Handled = True
RaiseEvent Klik(sender, EventArgs.Empty)
End Sub
End Class
This code gives error at RaiseEvent Klik(sender, EventArgs.Empty)
Error is : Cannot refer to an instance member of a class from within a shared method or shared member initializer without an expliticit instance of the class.
Thanks for your answers,
Alper
The problem is clearly stated in the exception message. The t_Click method is Shared (which means common to all instances of the class), so it cannot raise an Event that is specific to an instance of the class. You should only raise the event from a method that is not shared.
Do something like this -
Imports System
Imports System.Text.RegularExpressions
Public Class CustomTextBlock
Inherits TextBlock
Public Event Klik As EventHandler(Of System.EventArgs)
Public ReadOnly InlineCollectionProperty As DependencyProperty = DependencyProperty.Register("InlineCollection", GetType(String), GetType(CustomTextBlock), New PropertyMetadata(New PropertyChangedCallback(AddressOf CustomTextBlock.InlineChanged)))
Private Shared Sub InlineChanged(ByVal sender As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
Dim d As CustomTextBlock = DirectCast(sender, CustomTextBlock)
d.Inlines.Clear()
d.OnInlineChanged(CType(e.NewValue, String))
End Sub
Private Sub OnInlineChanged(ByVal Value As String)
Dim kelimeler = Split(Value, " ")
For i As Integer = 0 To kelimeler.Length - 1
If Regex.Match(kelimeler(i), "(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,#?^=%&:/~\+#]*[\w\-\#?^=%&/~\+#])?").Success Then
Dim x = New Hyperlink(New Run(kelimeler(i)))
x.AddHandler(Hyperlink.ClickEvent, New RoutedEventHandler(AddressOf t_Click))
x.ToolTip = kelimeler(i)
x.Tag = kelimeler(i)
Me.Inlines.Add(x)
If Not i = kelimeler.Length Then Me.Inlines.Add(" ")
Else
Me.Inlines.Add(kelimeler(i))
If Not i = kelimeler.Length Then Me.Inlines.Add(" ")
End If
''//Console.WriteLine(kelime(i).ToString.StartsWith("#"))
Next
kelimeler = Nothing
End Sub
Public Property InlineCollection As String
Get
Return DirectCast(GetValue(InlineCollectionProperty), String)
End Get
Set(ByVal value As String)
SetValue(InlineCollectionProperty, value)
End Set
End Property
Private Sub t_Click(ByVal sender As Hyperlink, ByVal e As System.Windows.RoutedEventArgs)
e.Handled = True
RaiseEvent Klik(sender, System.EventArgs.Empty)
End Sub
End Class