PropertyChangedCallback not firing when respective property bound to ScrollBar - c#

I have a rather simple UserControl which I would like to extend with the DependencyProperty. The relevant code of the control is as follows:
public partial class CompassControl : UserControl
{
public static readonly DependencyProperty AngleProperty =
DependencyProperty.Register("Angle", typeof(Double), typeof(CompassControl),
new FrameworkPropertyMetadata( 0.0, FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(OnAngleChanged)));
private static void OnAngleChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
((CompassControl)target).SetImageAngle((Double)e.NewValue);
}
public CompassControl( )
{
InitializeComponent( );
}
public Double Angle
{
get { return (Double)GetValue(AngleProperty); }
set { SetValue(AngleProperty, value); }
}
This control is being used on a simple form; the relevant XAML as follows:
<DockPanel DockPanel.Dock="Bottom">
<DockPanel>
<TextBlock DockPanel.Dock="Left"
TextAlignment="Center" FontWeight="Bold" FontSize="12"
Padding="0,4,0,0" HorizontalAlignment="Left"
Height="22" Width="60" Margin="10,0,0,0"
Text="{Binding ElementName=scrollBarAngle, Path=Value}">
</TextBlock>
<ScrollBar DockPanel.Dock="Left" Name="scrollBarAngle" Orientation="Horizontal"
Height="22" Margin="10,0"
Maximum="360.0" Minimum="0.0" SmallChange="1.0" Value="0.0"
ValueChanged="scrollBarAngle_ValueChanged" />
</DockPanel>
</DockPanel>
<ctl:CompassControl DockPanel.Dock="Top" Name="compassControl"
Margin="5" Width="Auto" Height="Auto"
Angle="{Binding ElementName=scrollBarAngle, Path=Value}"
/>
</DockPanel>
The "Text" property of the TextBox and the "Angle" property of my control are bound to the "Value" property of the ScrollBar using the following binding:
"{Binding ElementName=scrollBarAngle, Path=Value}"
When I scroll the ScrollBar, the Text field is updated as expected, but the Angle does not change - the OnAngleChanged callback is not being called!
However if I directly change the Angle property in the ScrollBar's ValueChanged event everything works fine - the property got changed and the respective callback fired:
private void scrollBarAngle_ValueChanged( object sender, RoutedPropertyChangedEventArgs<double> e )
{
compassControl.Angle = e.NewValue;
}
Please help resolve this issue!
Thank you,
--Alex

My apologies - the problem was not in the code, but in the environment! I had several copies of VS2013 open, the project was open in two of them. Anyway, after reading Clemens comment indicating that my problem is not reproducible, I closed all instances of VS, then started the fresh instance, opened the project - and everything worked fine!
Thank you!

Related

OnPropertyChange(null) makes hyperlink disappear

Code first (upper TextBox is simplified for purpose of this question) :
<TextBlock
Style="{StaticResource FieldNameStyle }"
TextWrapping ="Wrap" Height="33" FontSize="12"
Visibility="Visible"
TextAlignment="Center"
Foreground="#FFFFFF"
Opacity="0.5"
Text="{Binding UnderLineMsg}">
<Hyperlink Name="PrivacyNoticeLink2"
Command="{Binding OpenPrivacyNoticeCommand}">
<TextBlock
Visibility="Visible"
Name="privacyNoticeText2"
Text="{Binding PrivacyNoticeButtonLabel,FallbackValue='privacy notice' ,UpdateSourceTrigger=PropertyChanged}"/>
</Hyperlink>
</TextBlock>
this is what it looks after the window loads for the first time : Under line msg filler: link
one of the events in the window triggers a call to
OnPropertyChanged(null);
the method triggers a "refresh" in all the members in the window that are subscribed to it with :
UpdateSourceTrigger=PropertyChanged
once called the Hyperlink element disappears completely (verified using Snoop 2.8)
so after the call it will look like this:
Under line msg filler:
i have NO idea why this is happening. the current fix is replacing the general OnPropertyChanged call with many specific ones but that is not a realistic option in the long run.
EDIT :
Isolated the issue to a new project, note the issue still happens when its only a textblock within a textblock
simple XAML with a button that triggers OnPropertyChanged
<Grid>
<Button Click="Meh" Margin="171,37,153,199">
PRESS ME
</Button>
<TextBlock Name="WrapperText" Text= "{Binding randomNumber}">
<TextBlock Name="linkText" Text="{Binding randomNumStr }"></TextBlock>
</TextBlock>
</Grid>
Code behind:
public MainWindow()
{
DataContext = new Stuff();
InitializeComponent();
}
public void Meh(object sender, RoutedEventArgs e)
{
//MessageBox.Show(this, "BLA", "caption", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
//MessageBox.Show("FASDFASDFASDF");
(DataContext as Stuff).OnPropertyChanged(null);
//Msg.ShowMessageBox("BLA", "caption", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
}
the "view model"
public class Stuff : INotifyPropertyChanged
{
public Stuff()
{
rnd = new Random();
}
private Random rnd;
public int randomNumber => rnd.Next(1, 100);
public string randomNumStr => randomNumber.ToString()+"Text";
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
public virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Note the truly disgusting way that I trigger the property change. I know I should use Icommand in the 'Stuff' class but I wanted to isolate the problem quickly. In my original code, it's done properly.
Don't bind the Text property of a TextBlock that you are also adding a Hyperlink to. Raising the PropertyChanged event for the source property will then clear out the Hyperlink.
Instead of binding the Text property of the TextBlock itself, you could add a Run element to it:
<TextBlock
TextWrapping ="Wrap" Height="33" FontSize="12"
Visibility="Visible"
TextAlignment="Center"
Foreground="#FFFFFF"
Opacity="0.5">
<Run Text="{Binding UnderLineMsg, Mode=OneWay}" />
<Hyperlink Name="PrivacyNoticeLink2" Command="{Binding OpenPrivacyNoticeCommand}">
<TextBlock
Visibility="Visible"
Name="privacyNoticeText2"
Text="{Binding PrivacyNoticeButtonLabel,FallbackValue='privacy notice' ,UpdateSourceTrigger=PropertyChanged}"/>
</Hyperlink>
</TextBlock>

C# UWP Button binding with flyout not refreshing button content

I have a button that displays the value from a class that I created. Everything works fine, except for the fact that the button content does not refresh once the value of the binding is changed in the code. If I exit the screen and come back, the value is correct. Staying on the same screen does not refresh the button content.
The button code is shown below.
<Grid x:Name="Task1Grid" Grid.Row="0" Grid.Column="0" Margin="5,0,5,0">
<Grid.RowDefinitions>
<RowDefinition Height=".2*"/>
<RowDefinition Height=".6*"/>
<RowDefinition Height=".2*"/>
</Grid.RowDefinitions>
<Button Grid.Row="1" Style="{StaticResource RoundedButtonStyle}" Tag="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Click="StoplightButton_Click" FontFamily="Global User Interface">
<Button.Content>
<Image Stretch="Uniform" Source="{Binding SelectedRepairOrder.TaskStatusGrid[0], Converter={StaticResource TaskStatusToStopLight}, Mode=OneWay}"/>
</Button.Content>
<Button.Background>
<ImageBrush Stretch="Uniform" ImageSource="{Binding SelectedRepairOrder.TaskStatusGrid[0], Converter={StaticResource TaskStatusToStopLight}, Mode=OneWay}"/>
</Button.Background>
</Button>
<Button x:Name="Task0Time" Tag="0" Style="{StaticResource RoundedButtonStyle}" Visibility="{Binding SelectedRepairOrder.TaskStatusGrid[0].NewTaskstatus, Converter=
{StaticResource TaskStatusToVisibility}}" IsEnabled="{Binding ShowForecastFeatures}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Content="{Binding SelectedRepairOrder.TaskStatusGrid[0].TmTimecmpltask, Converter={StaticResource TaskCompleteTimeToTime}}" Grid.Row="2" Flyout="{StaticResource Task1Flyout}"/>
<TextBlock Grid.Row="0" Text="{Binding ClientInfo.TasksInfo[0].TaskDescription}" TextAlignment="Center" VerticalAlignment="Bottom" FontSize="28"/>
</Grid>
The flyout code is shown below.
<Border x:Name="StopLightBorder" Background="CornflowerBlue" Grid.Row="1" BorderBrush="White" BorderThickness="2">
<Grid x:Name="StopLightGrid" Margin="5" >
<Grid.Resources>
<converter:TaskStatusToStopLight x:Key="TaskStatusToStopLight"/>
<converter:TaskCompleteTimeToTime x:Key="TaskCompleteTimeToTime"/>
<converter:TaskStatusToVisibility x:Key="TaskStatusToVisibility"/>
<Flyout x:Key="Task1Flyout" >
<ListBox ItemsSource="{Binding ForecastTimes}" Tag="0" SelectionChanged="ForecastTimeChanged"/>
</Flyout>
The code which changes the value for the binding is shown below.
private void ForecastTimeChanged(object sender, SelectionChangedEventArgs e)
{
var timeListBox = (ListBox)sender;
var completeTime = Convert.ToDateTime(e.AddedItems[0].ToString());
var taskNum = Convert.ToInt16(((FrameworkElement)sender).Tag);
var result = checkPreviousTaskTimes(completeTime, taskNum);
switch (result)
{
case ForecastResult.ValidTime:
globalContext.SelectedRepairOrder.TaskStatusGrid[taskNum].TmTimecmpltask = completeTime.ToString();
globalContext.SelectedRepairOrder.TaskStatusGrid[taskNum].DtDateoverride = completeTime.ToString();
globalContext.SelectedRepairOrder.TaskStatusGrid[taskNum].TmTimeoverride = completeTime.ToString();
globalContext.SelectedRepairOrder.TaskStatusGrid[taskNum].SendOverrideForecastTime = true;
globalContext.SelectedRepairOrder.WasChanged = true;
globalContext.SelectedRepairOrder.RecordGrid = "1";
((Popup)((FlyoutPresenter)((FrameworkElement)sender).Parent).Parent).IsOpen = false;
break;
default:
showForecastError(result, completeTime, taskNum);
break;
}
}
The Visibility and IsEnabled both work just fine. Not sure what else I can do at this point. It seems that changing the bound data does not have an effect until you leave the screen. I chased this issue all the way through and saw the changes to the data as well as everything else I expected. The flyout causes the forecasttimechanged method to activate. When we go to save this data to the database, the data is correct. The flyout shows the selected time when viewing it on the screen, which is what I want. I see that highlighted in the flyout.
If there is a better control to use than the button, I am all ears at this point. Here is the tricky part. This forecast time can be set in the application as well as the app you are seeing code from. The app has time in 15 minute increments, but the other program that can update this control can put in any time it wishes.
I know there is some control or parameter that needs to be set in order to make this happen properly, but for the life of me, I cannot find it. I have tried everything for the past 3 days now and nothing works.
Help me please.
I know there is some control or parameter that needs to be set in order to make this happen properly, but for the life of me, I cannot find it. I have tried everything for the past 3 days now and nothing works.
From your code, I guess the problem is that you have not implemented INotifyPropertyChanged for binding property. And your logic is complex, you could realize your feature with the easy way like the follow example.
<Button Content="{Binding SelectItem,Mode=OneWay}">
<Button.Flyout>
<Flyout Placement="Top">
<ListBox ItemsSource="{Binding Items}" SelectedItem="{Binding SelectItem,Mode=TwoWay}">
</ListBox>
</Flyout>
</Button.Flyout>
</Button>
Bind the button content with SelectItem, And then the button content will be modified automatically if the ListBox SelectedItem changed.
public class MainPageViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public List<string> Items { get; set; } = new List<string>();
private string selectItem = "Nico";
public string SelectItem { get { return selectItem; } set { selectItem = value; OnPropertyChanged(); } }
public MainPageViewModel()
{
Items.Add("Nico");
Items.Add("Song");
Items.Add("Xiao");
}

Textbox is becoming readonly in flyout, UWP store app

In button flyout I am using one usercontrol inside that I have textbox. when running the app the textbox is appearing as readonly, don't know why I am getting this issue. nowhere I am setting readonly.
<TextBox Margin="2" Height="32"
MaxHeight="60"
TextWrapping="Wrap"
HorizontalAlignment="Stretch"
TextAlignment="Left"
Text="ramesh"
Style="{x:Null}"/>
Figure out the issue it's because of anniversary update.
https://blogs.msdn.microsoft.com/wsdevsol/2016/09/14/combobox-from-an-appbarbutton-loses-mouse-input-on-1607/
I created attached property for solution given in above link. Below is the attached property
public class CompatExtensions
{
public static bool GetAllowFocusOnInteraction(DependencyObject obj)
{
return (bool)obj.GetValue(AllowFocusOnInteractionProperty);
}
public static void SetAllowFocusOnInteraction(DependencyObject obj, bool value)
{
obj.SetValue(AllowFocusOnInteractionProperty, value);
}
// Using a DependencyProperty as the backing store for AllowFocusOnInteraction.
// This enables animation, styling, binding, etc...
public static readonly DependencyProperty AllowFocusOnInteractionProperty =
DependencyProperty.RegisterAttached("AllowFocusOnInteraction",
typeof(bool),
typeof(CompatExtensions),
new PropertyMetadata(0, AllowFocusOnInteractionChanged));
private static bool allowFocusOnInteractionAvailable =
Windows.Foundation.Metadata.ApiInformation.IsPropertyPresent(
"Windows.UI.Xaml.FrameworkElement",
"AllowFocusOnInteraction");
private static void AllowFocusOnInteractionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (allowFocusOnInteractionAvailable)
{
var element = d as FrameworkElement;
if (element != null)
{
element.AllowFocusOnInteraction = (bool)e.NewValue;
}
}
}
}
And an example of it used:
<AppBarButton local:CompatExtensions.AllowFocusOnInteraction="True" Icon="Setting">
<AppBarButton.Flyout>
<Flyout>
<StackPanel Orientation="Vertical" >
<ComboBox>
<ComboBoxItem Content="Red" IsSelected="True" />
<ComboBoxItem Content="Green" />
<ComboBoxItem Content="Blue"/>
</ComboBox>
</StackPanel>
</Flyout>
</AppBarButton.Flyout>
</AppBarButton>
Difficult to be sure about any answer given how the few details have been provided but I once saw something similar due to sizing of the TextBox. The UWP text box has a "delete" button (a small cross) at the end of the box to delete the current content. When the TextBox was sized vertically, the delete button scaled to take up the entirety of the TextBox thereby making it look read only.
If you're facing a similar issue, try setting AcceptsReturn="True" and InputScope="Text" on the TextBox.

ZoomExtents method call works different than activating ZoomExtents through gesture

I've been working on a small 3D preview window in a MVVM style application... The view is created then its data context is set. Therefore it seems that ZoomExtentsWhenLoaded="True" doesn't seem to help do what I need. I need something like, ZoomExtentsWhenDataContextChanges.
Interestingly, I've found that if I use a mouse gesture like the one defined below, I can physically click on the HelixViewport3D and it will perform a ZoomExtents.
HelixViewport3D.ZoomExtentsGesture = new MouseGesture(MouseAction.LeftDoubleClick);
However, if do something like this...
HelixViewport3D.DataContextChanged += (o, e) => ResetCamera();
private void ResetCamera()
{
var dc = HelixViewport3D.DataContext as WellSurveyPlot3DViewModel;
HelixViewport3D.ResetCamera();
HelixViewport3D.Camera = dc.PerspectiveCamera;
HelixViewport3D.ZoomExtents();
}
The viewport does zoom, it just doesn't center itself, like it does when activating ZoomExtents when using the mouse gesture.
I tried ResetCamera, and several other things... What is the standard way of dealing with keeping a viewport around and swapping out the DataContext instead of creating a new one each time?
I fixed this with an attached property. I read through the HelixViewport3D source code and got this idea, after noticing how the camera works. It seems an update to the default camera through a property binding doesn't really do anything after the control is initialized.
public static class HelixViewport3DZoomExtent
{
private static readonly Type OwnerType = typeof(HelixViewport3DZoomExtent);
public static readonly DependencyProperty ZoomExtentsOnUpdateProperty = DependencyProperty.RegisterAttached("ZoomExtentsOnUpdate", typeof(bool), OwnerType, new PropertyMetadata(false, OnDataContextChanged));
public static bool GetZoomExtentsOnUpdate(DependencyObject obj)
{
return (bool)obj.GetValue(ZoomExtentsOnUpdateProperty);
}
public static void SetZoomExtentsOnUpdate(DependencyObject obj, bool value)
{
obj.SetValue(ZoomExtentsOnUpdateProperty, value);
}
private static void OnDataContextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var viewport = d as HelixViewport3D;
if (viewport == null) return;
if (viewport.DataContext == null) return;
viewport.Camera = viewport.DefaultCamera;
viewport.ZoomExtents();
}
}
Here is the Xaml
<Border BorderBrush="Black" BorderThickness="1">
<Grid>
<h:HelixViewport3D Name="HelixViewport3D"
PanGesture="LeftClick"
DataContext="{Binding PreviewPlot, UpdateSourceTrigger=PropertyChanged}"
DefaultCamera="{Binding PerspectiveCamera, UpdateSourceTrigger=PropertyChanged}"
services:HelixViewport3DZoomExtent.ZoomExtentsOnUpdate="{Binding RelativeSource={RelativeSource AncestorType={x:Type views:WellSurveyPlot3DPreview}},
Path=DataContext.PreviewUpdatedReZoom, UpdateSourceTrigger=PropertyChanged}">
<h:SunLight/>
<h:TubeVisual3D Path="{Binding TubePath}" Diameter="75" ThetaDiv="12" IsPathClosed="False" Fill="LightGray"/>
<h:GridLinesVisual3D Width="{Binding GridLength}" Length="{Binding GridLength}" MajorDistance="{Binding MajorGridLines}" Thickness="25"
MinorDistance="{Binding MajorGridLines, UpdateSourceTrigger=PropertyChanged}" LengthDirection="1,0,0" Normal="0,0,1"
Center="{Binding BottomPlaneCenter,UpdateSourceTrigger=PropertyChanged}" Fill="Red" />
<h:GridLinesVisual3D Width="{Binding GridLength}" Length="{Binding GridLength, UpdateSourceTrigger=PropertyChanged}" LengthDirection="0,0,1" Normal="1,0,0" Thickness="25"
MajorDistance="{Binding MajorGridLines}" MinorDistance="{Binding MajorGridLines}"
Center="{Binding BackLeftPlaneCenter, UpdateSourceTrigger=PropertyChanged}" Fill="Blue" />
<h:GridLinesVisual3D Width="{Binding GridLength}" Length="{Binding GridLength, UpdateSourceTrigger=PropertyChanged}" LengthDirection="1,0,0" Normal="0,1,0" Thickness="25"
MajorDistance="{Binding MajorGridLines}" MinorDistance="{Binding MajorGridLines}"
Center="{Binding BackRightPlaneCenter,UpdateSourceTrigger=PropertyChanged}" Fill="Green" />
</h:HelixViewport3D>
<Button Content="Open Well Viewer" HorizontalAlignment="Left" VerticalAlignment="Top" Command="{Binding OpenWindowCmd}"/>
</Grid>
</Border>
In my view model I have to toggle my PreviewUpdateReZoom property.
private void LoadSurveyPoints(List<WellSurveyPointCalculated> surveyPoints)
{
_coordinatesCalculator = _calcGlobalCoordsFactory.Create(surveyPoints);
_wellXyzCoordinates = _coordinatesCalculator.PlotGlobalCoordinates(100).ToList();
PreviewPlot = WellSurveyPlot3DViewModel();
PreviewUpdatedReZoom = false;//Toggle true false to send property changed and get attached property to fire.
PreviewUpdatedReZoom = true;
}
This now works such that every new item drawn into the viewport has the correct camera settings and zooms to extents...

Binding to RichTextBox Blocks property

I would like to bind directly to the Blocks property of a RichTextBox in my Xaml. This is not possible as the Blocks property is read only. I can bind directly to an individual run:
<RichTextBox x:Name="MyRichTextBox" FontSize="36" Margin="10" Foreground="White">
<Paragraph>
<Run Text="{Binding MyObject.Text}" Foreground="Yellow"/>
<Run Text="{Binding MyObject.Text}" Foreground="Cyan"/>
</Paragraph>
</RichTextBox>
I would like to do something like:
<RichTextBox x:Name="MyRichTextBox" Blocks="{Binding MyObject.RichTextBlocks}" FontSize="36" Margin="10" Foreground="White"/>
Particularly as I don't know in advance how many blocks will be returned from the binding object.
Is the correct way to achieve this to create an Attached Behaviour for the RichTextBox with a RichTextBlocks property that when being set enumerates through the blocks and calls RichTextBox.Blocks.Add() for each one?
I am new to C#, .NET and XAML so please excuse the elementary question and a simply explained answer would be greatly appreciated.
With the pointers from #Nogard and the other post, I created my own class with a Dependency Property called RichText. Have posted here in case it is of use to anyone else.
public class MyRichTextBox : RichTextBox
{
public static readonly DependencyProperty RichTextProperty = DependencyProperty.Register("RichText", typeof(Paragraph), typeof(MyRichTextBox), new PropertyMetadata(null, RichTextPropertyChanged));
public Paragraph RichText
{
get
{
return (Paragraph)GetValue(RichTextProperty);
}
set
{
SetValue(RichTextProperty, value);
}
}
private static void RichTextPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
MyRichTextBox richTextBox = (MyRichTextBox)dependencyObject;
Paragraph paragraph = (Paragraph)dependencyPropertyChangedEventArgs.NewValue;
// Remove any existing content from the text box
richTextBox.Blocks.Clear();
// Add the paragraph to the text box
richTextBox.Blocks.Add(paragraph);
}
}
}
and added this to my xaml...
<sub:MyRichTextBox x:Name="MyRichTextOverlay" RichText="{Binding CurrentOverlay.RichTextParagraph}" VerticalAlignment="Top" FontSize="36" Margin="10" Foreground="White" HorizontalAlignment="Center" TextWrapping="Wrap" TextAlignment="Center"/>

Categories