I am making a window which is supposed to record the mouse position. I have created a user control which is supposed to display the current coordinates for that position tag and allow the user to change them.
It works great except for the fact that when one is updated they all change.
I think this has something to do with the fact that dependency properties are statically registered. They need to be dependency properties because I need to be able to Bind them to my model from the xaml.
How can I have the User Controls independent from one another?
<UserControl x:Class="SapFormFiller.SerializableMouseEditorControl"
...
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60*"/>
...
<ColumnDefinition Width="20*"/>
</Grid.ColumnDefinitions>
<Label Content="{Binding LabelText}"></Label>
<Label Grid.Column="1" Content="{Binding SerializableMouseKeyboardEventArgs.X}"/>
<Label Grid.Column="2" Content="{Binding SerializableMouseKeyboardEventArgs.Y}"/>
<Button Grid.Column="3" Margin="0,0,0.4,0" Click="ButtonBase_OnClick">Edit</Button>
</Grid>
cs:
public partial class SerializableMouseEditorControl : UserControl
{
public static DependencyProperty LabelTextProperty = DependencyProperty.Register(
"LabelText", typeof (string), typeof (SerializableMouseEditorControl), new PropertyMetadata(default(string)));
public string LabelText
{
get { return (string) GetValue(LabelTextProperty); }
set { SetValue(LabelTextProperty, value); }
}
public static DependencyProperty SerializableMouseKeyboardEventArgsProperty = DependencyProperty.Register(
"SerializableMouseKeyboardEventArgs", typeof (SerializableMouseKeyboardEventArgs), typeof (SerializableMouseEditorControl), new PropertyMetadata(new SerializableMouseKeyboardEventArgs()));
public SerializableMouseKeyboardEventArgs SerializableMouseKeyboardEventArgs
{
get { return (SerializableMouseKeyboardEventArgs) GetValue(SerializableMouseKeyboardEventArgsProperty); }
set { SetValue(SerializableMouseKeyboardEventArgsProperty, value); }
}
public SerializableMouseEditorControl()
{
InitializeComponent();
SerializableMouseKeyboardEventArgs = new SerializableMouseKeyboardEventArgs();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
SerializableMouseKeyboardEventArgs.Update();
}
}
SerializableMouseKeyboardEventArgs:
public class SerializableMouseKeyboardEventArgs : INotifyPropertyChanged
{
public int X
{
get { return _x; }
set
{
_x = value;
OnPropertyChanged("X");
}
}
public int Y
{
get { return _y; }
set
{
_y = value;
OnPropertyChanged("Y");
}
}
...
IKeyboardMouseEvents gkme;
private int _x = 0;
private int _y=0;
public override string ToString(){...}
public SerializableMouseKeyboardEventArgs()
{
gkme = Hook.GlobalEvents();
}
public void Update()
{
IsEditing = true;
gkme.MouseClick += Gkme_MouseClick;
}
private void Gkme_MouseClick(object sender, MouseEventArgs e)
{
if(e.Button==MouseButtons.Left)
{
this.X = e.X;
this.Y = e.Y;
}
else if (e.Button == MouseButtons.Right)
{
gkme.MouseClick -= Gkme_MouseClick;
IsEditing = false;
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
This line
new PropertyMetadata(new SerializableMouseKeyboardEventArgs()));
looks like it could be causing you problems. I was in a situation where declaring default values using new keyword was causing one instance of the UserControl to share that property value with another instance.
Try initializing any such properties in your constructor and it might work.
Related
I am trying to access property value of a child window's view model from the parent View Model.I am calling window from parent view model.I want to make changes in main window based on the operation in child view model. I couldn't get any value of child view model in parent view model.I am trying this in MVVM pattern.
Interface for dialog
public interface IWindowService
{
void OpenDialogWindow(DialogViewModel vm);
}
Parent view model
public class FunctionalViewModel : INotifyPropertyChanged
{
private readonly IWindowService _windowService;
private string connectionString;
public string ConnectionString
{
get { return connectionString; }
set
{
connectionString = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ConnectionString"));
}
}
public FunctionalViewModel(IWindowService windowService)
{
BuildConnectionCommand = new RelayCommand(new Action<object>(BuildConnectionString));
_windowService = windowService;
}
private void BuildConnectionString(object obj)
{
MessageBox.Show("will open a window");
_windowService.OpenDialogWindow(new DialogViewModel());
}
}
Child View Model
public class DialogViewModel : FunctionalViewModel,INotifyPropertyChanged
{
private string textboxsaf;
public string Textboxsaf
{
get { return textboxsaf; }
set {
textboxsaf = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Textboxsaf"));
}
}
private ICommand connectionCommand;
public ICommand ConnectionCommand
{
get { return connectionCommand; }
set { connectionCommand = value; }
}
public DialogViewModel()
{
ConnectionCommand = new RelayCommand(new Action<object>(SetValue));
}
public event PropertyChangedEventHandler PropertyChanged;
public void SetValue(object test)
{
textboxsaf= "ValueFromPopUpWindo";
Application.Current.Windows[1].Close();
}
}
ChildWindow.xaml
<Grid>
<Label x:Name="label" Content="my popup window" HorizontalAlignment="Left" Margin="73,68,0,0" VerticalAlignment="Top" Width="132"/>
<TextBox x:Name="textBox" HorizontalAlignment="Left" Height="23" Margin="73,121,0,0"
TextWrapping="Wrap"
Text="{Binding Path=Textboxsaf,Mode=TwoWay}" VerticalAlignment="Top" Width="120"/>
<Button x:Name="button" Content="Button" HorizontalAlignment="Left"
Margin="109,177,0,0" VerticalAlignment="Top" Width="75"
Command="{Binding Path=ConnectionCommand }"
/>
</Grid>
</Window>
MainWindow.xaml
<Grid>
<Button Name="btnConnectionString" Grid.Row="0" Grid.Column="2" Content="Connection string" Height="40" Width="150"
Command="{Binding Path=BuildConnectionCommand}"
DataContext="{Binding tfs}"></Button>
</Grid>
Code behind file of main window
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel()
{
rel = new ReleaseViewModel(),
tfs = new FunctionalViewModel(new WindowService()),
wnd = new DialogViewModel()
};
}
}
public class WindowService : IWindowService
{
public void OpenDialogWindow(DialogViewModel vm)
{
ConnectionWindow win = new ConnectionWindow();
win.DataContext = vm;
win.Show();
}
}
Question
I would like to access the value of the property Textboxsaf in the child view model(DialogViewModel) from parent view model(FunctionalViewModel) . Assign value of Textboxsaf to ConnectionString from the funcitonalviewModel . after closing window is good.
I wouldn't use PropertyChanged to retrieve the value of DialogViewModel.Textboxsaf as this proprty might change multiple times during the lifetime of the dialog.
I would make IWindowService.OpenDialogWindow return a custom DialogResult object or the original DialogViewModel probably converting the IWindowService.OpenDialogWindow to an asynchronous method.
Alternatively implement a IWindowService.DialogClosed event:
FunctionalViewModel.cs
public class FunctionalViewModel : INotifyPropertyChanged
{
private readonly IWindowService _windowService;
private string connectionString;
public string ConnectionString
{
get { return connectionString; }
set
{
connectionString = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.ConnectionString)));
}
}
private void BuildConnectionString(object obj)
{
MessageBox.Show("will open a window");
_windowService.DialogClosed += OnDialogClosed;
_windowService.OpenDialogWindow(new DialogViewModel());
}
private void OnDialogClosed(object sender, DialogResultEventArgs e)
{
_windowService.DialogClosed -= OnDialogClosed;
ConnectionString = e.Result.Textboxsaf;
}
}
WindowService.cs
public class WindowService : IWindowService
{
public event EventHandler<DialogResultEventArgs> DialogClosed;
public void OpenDialogWindow(DialogViewModel vm)
{
ConnectionWindow win = new ConnectionWindow();
win.DataContext = vm;
win.Closed += OnConnectionWindowClosed;
win.Show();
}
protected virtual void OnConnectionWindowClosed(object sender, EventArgs e)
{
var dialog = sender as FrameworkElement;
this.DialogClosed?.Invoke(this, new DialogResultEventArgs(dialog.DataContext as DialogViewModel));
}
}
DialogResultEventArgs.cs
public class DialogResultEventArgs : EventArgs
{
public DialogViewModel Result { get; }
public DialogResultEventArgs(DialogViewModel result) => this.Result = result;
}
You could keep a reference to the DialogViewModel and subscribe to its PropertyChanged event:
private void BuildConnectionString(object obj)
{
var childViewModel = new DialogViewModel();
childViewModel.PropertyChanged += OnChildPropertyChanged;
MessageBox.Show("will open a window");
_windowService.OpenDialogWindow(childViewModel);
}
private void OnChildPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(DialogViewModel.Textboxsaf))
{
childViewModel.PropertyChanged -= OnChildPropertyChanged;
ConnectionString = (sender as DialogViewModel)?.DialogViewModel;
}
}
I have a function that takes an object of storagefile and creates a thumbnail of it. This function is in the converter value since we have 10.000+ files that are going to be shown in a media gallery. The thumbnail will be generated on the file when the thumbnail is not yet created for the storage file.
The problem is, the UI of GridView is not updated once the thumbnail is generated / the object is updated. It will only show the thumbnail if I scroll down far enough and scroll back up again.
public class MediaFile : INotifyPropertyChanged
{
// Declare the event
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private ImageSource _thumbnail = null;
public ImageSource Thumbnail
{
get { return _thumbnail; }
set
{
_thumbnail = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("Thumbnail");
}
}
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged(this, e);
}
// Create the OnPropertyChanged method to raise the event
// The calling member's name will be used as the parameter.
protected void OnPropertyChanged(string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
This is the class of the so called MediaFile. As you can see I already implemented the property changed event.
<DataTemplate x:Key="GridMedia_DataTemplate" x:DataType="MediaFile">
<Grid Style="{StaticResource Faves_Grid}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Image Grid.Row="1" Grid.RowSpan="2" Margin="0 14 0 -12" Grid.ColumnSpan="2" HorizontalAlignment="Stretch" Stretch="UniformToFill" VerticalAlignment="Stretch" Source="{x:Bind Converter={StaticResource ThumbnailGenerator}, Mode=OneWay}"/>
</Grid>
</DataTemplate>
Here is the data template for the grid view. As you can see, for the ImageSource we use converter to get / generate the thumbnail.
public object Convert(object value, Type targetType, object parameter, string language)
{
try
{
if(value is MediaFile)
{
MediaFile file = value as MediaFile;
//generate Thumbnail
int width = 160;
int height = 90;
var pathFileTemp = $#"{FileLocations.MediaPath}";
try
{
StorageFile originalFile = StorageFile.GetFileFromPathAsync(pathFileTemp + file.Id).AsTask().Result;
if (originalFile != null)
{
//check extention
if (file.Extension.ToLower().Contains("pdf"))
{
GetThumbnailForPDF(file).GetAwaiter();
return file.Thumbnail;
}
else if (file.Extension.ToLower().Contains("jpg") ||
file.Extension.ToLower().Contains("gif") ||
file.Extension.ToLower().Contains("png") ||
file.Extension.ToLower().Contains("jpeg"))
{
GetThumbnailForImage(file).GetAwaiter();
return file.Thumbnail;
}
}
return new BitmapImage(new Uri("ms-appx:///Assets/Icon/placeholder_img.jpg", UriKind.Absolute));
}
catch (Exception)
{
return new BitmapImage(new Uri("ms-appx:///Assets/Icon/placeholder_img.jpg", UriKind.Absolute));
}
}
return null;
}
catch (Exception)
{
return null;
}
}
As for now we only create thumbnail for pdf and images. Other than that we will return placeholder thumbnail.
<controls1:AdaptiveGridView Grid.Row="1" DesiredWidth="288" x:Name="ListView_MediaLibrary" Grid.ColumnSpan="4" ItemTemplate="{StaticResource GridMedia_Adaptive__DataTemplate}" Margin="0 0 -8 -20" SelectionChanged="ListView_MediaLibrary_SelectionChanged" ItemsSource="{Binding Source={StaticResource mediaFilesSource}, Mode=OneWay}">
</controls1:AdaptiveGridView>
And this is how we bind the list with the GridView.
Update
The thumbnail is visible only if I get out from the view by scrolling down, and then scrolling up
MediaFile.cs
public class MediaFile : INotifyPropertyChanged
{
// Declare the event
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private ImageSource _thumbnail = null;
public ImageSource Thumbnail
{
get { return _thumbnail; }
set
{
_thumbnail = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("Thumbnail");
}
}
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged(this, e);
}
// Create the OnPropertyChanged method to raise the event
// The calling member's name will be used as the parameter.
protected void OnPropertyChanged(string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public override bool Equals(object obj)
{
return obj is MediaFile mediaFile &&
Id == mediaFile.Id;
}
}
}
GridMedia.xaml UserControl
<UserControl>
<Grid Style="{StaticResource Faves_Grid}">
<Image HorizontalAlignment="Stretch" x:Name="ThumbnailImage" Stretch="UniformToFill" VerticalAlignment="Stretch"/>
</Grid></UserControl>
GridMedia.xaml.cs UserControl
public sealed partial class GridMedia : UserControl
{
public GridMedia()
{
this.InitializeComponent();
}
public MediaFile Data
{
get { return (MediaFile)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
// Using a DependencyProperty as the backing store for Data. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(MediaFile), typeof(GridMedia), new PropertyMetadata(null, new PropertyChangedCallback(Data_Changed)));
private static void Data_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != null && !e.NewValue.Equals(e.OldValue) && e.NewValue is MediaFile data)
{
var instance = d as GridMedia;
// get a bitmapImage through the convert method
instance.ThumbnailImage.Source = Thumbnail(e.NewValue);
}
}
static ImageSource Thumbnail(object value)
{
try
{
if (value is MediaFile)
{
MediaFile file = value as MediaFile;
//generate Thumbnail
int width = 160;
int height = 90;
var pathFileTemp = $#"{FileLocations.MediaPath}";
try
{
StorageFile originalFile = StorageFile.GetFileFromPathAsync(pathFileTemp + file.Id).AsTask().Result;
if (originalFile != null)
{
//check extention
if (file.Extension.ToLower().Contains("pdf"))
{
GetThumbnailForPDF(file).GetAwaiter();
return file.Thumbnail;
}
else if (file.Extension.ToLower().Contains("jpg") ||
file.Extension.ToLower().Contains("gif") ||
file.Extension.ToLower().Contains("png") ||
file.Extension.ToLower().Contains("jpeg"))
{
GetThumbnailForImage(file).GetAwaiter();
return file.Thumbnail;
}
}
return new BitmapImage(new Uri("ms-appx:///Assets/Icon/placeholder_img.jpg", UriKind.Absolute));
}
catch (Exception)
{
return new BitmapImage(new Uri("ms-appx:///Assets/Icon/placeholder_img.jpg", UriKind.Absolute));
}
}
return null;
}
catch (Exception)
{
return null;
}
}
Please try to use Converter to handle simple conversion instead of asynchronous conversion involving IO.
You can try to convert your DataTemplate into UserControl, and convert the file operation logic of Converter into the callback of dependency property change inside the control.
Please try the following steps:
Create a comparison method for the MediaFile class (Suppose there is a unique Id property)
public class MediaFile : INotifyPropertyChanged
{
//... other code
public override bool Equals(object obj)
{
return obj is MediaFile mediaFile &&
Id == mediaFile.Id;
}
}
Create a UserControl
MyMediaFileItem.xaml
<UserControl
...>
<Grid>
<!-- your other code -->
<Image x:Name="ThumbnailImage"/>
</Grid>
</UserControl>
MyMediaFileItem.xaml.cs
public MediaFile Data
{
get { return (MediaFile)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
// Using a DependencyProperty as the backing store for Data. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(MediaFile), typeof(MyMediaFileItem), new PropertyMetadata(null,new PropertyChangedCallback(Data_Changed)));
private static void Data_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if(e.NewValue!=null && !e.NewValue.Equals(e.OldValue) && e.NewValue is MediaFile data)
{
var instance = d as MyMediaFileItem;
// get a bitmapImage through the convert method
instance.ThumbnailImage.Source = bitmapImage;
}
}
Usage
<DataTemplate x:Key="GridMedia_DataTemplate" x:DataType="MediaFile">
<controls:MyMediaFileItem Data="{Binding}"/>
</DataTemplate>
Thanks.
Xaml as below:
<ItemsControl
x:Class="PowersOf2.Windows10.Views.Controls.Board"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:PowersOf2.Windows10.Views.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="Root" ItemsSource="{Binding Fields, ElementName=Root}" Loaded="Root_Loaded"
>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid
Width="{Binding FieldWidth, ElementName=Root}"
Height="{Binding FieldHeight, ElementName=Root}"
Loaded="Grid_Loaded" Background="White"
>
<Grid.RenderTransform>
<TranslateTransform X="{Binding X}" Y="{Binding Y}"/>
</Grid.RenderTransform>
<TextBlock Text="{Binding Text}" Foreground="Black"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Fields is IEnumerable of Field which has coordinates X and Y. They are managed by view model. FieldWidth and FieldHeight are dependency properties calculated in code behind.
How to get binding object of nested dependency properties such as TranslateTransform.X and TranslateTransform.Y in code behind?
UPDATE:
Based on this question: Fredrik's answer works as expected until you work with single embedded object in xaml with binding to non-nested properties, but not for nested ones. This issue is more complicated due to ItemsControl containing my Grid.
Code behind below:
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using Windows.UI.Xaml;
namespace Controls
{
public sealed partial class Board
{
public Board()
{
InitializeComponent();
}
private void Root_Loaded(object sender, RoutedEventArgs e)
{
FieldWidth = 100.0;
FieldHeight = 100.0;
Fields =
new Field[]
{
new Field { X = 100, Y = 100, Text = "one" },
new Field { X = 300, Y = 300, Text = "two" }
};
}
public double FieldWidth
{
get { return (double)GetValue(FieldWidthProperty); }
set { SetValue(FieldWidthProperty, value); }
}
public static readonly DependencyProperty FieldWidthProperty = DependencyProperty.Register(
"FieldWidth", typeof(double), typeof(Board), new PropertyMetadata(0.0)
);
public double FieldHeight
{
get { return (double)GetValue(FieldHeightProperty); }
set { SetValue(FieldHeightProperty, value); }
}
public static readonly DependencyProperty FieldHeightProperty = DependencyProperty.Register(
"FieldHeight", typeof(double), typeof(Board), new PropertyMetadata(0.0)
);
public IEnumerable<Field> Fields
{
get { return (ObservableCollection<Field>)GetValue(FieldsProperty); }
set { SetValue(FieldsProperty, value); }
}
public static readonly DependencyProperty FieldsProperty = DependencyProperty.Register(
"Fields", typeof(IEnumerable<Field>), typeof(Board), new PropertyMetadata(null)
);
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
// here I want to get binding of RenderTransform's properties
}
}
public class Field : INotifyPropertyChanged
{
private int _x;
public int X
{
get { return _x; }
set
{
_x = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("X"));
}
}
private int _y;
public int Y
{
get { return _y; }
set
{
_y = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Y"));
}
}
private string _text;
public string Text
{
get { return _text; }
set
{
_text = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Text"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
I hope I haven't misinterpreted the question but you can get the transformation and the bound item like this.
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
var grid = (Grid)sender;
//the actual transformation
var render = (Transform)grid.GetValue(RenderTransformProperty);
//the field the transformation is bound to
var field = (Field)grid.DataContext;
//for now this only works in WPF
var binding = BindingOperations.GetBinding(render, TranslateTransform.XProperty);
}
Made an edit for this, but it does not work for winrt.
The method BindingOperations.GetBinding is only available in WPF.
Hope that winrt gets this soon.
I have a problem and I'm not sure what it is. I have a class within a class that has a value that needs to be bound to a control, in this case visibility. The code is changing the value correctly but the output does not change (i.e collapse the control)
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<StackPanel Orientation="Vertical">
<Button x:Name="buttonOne" Content="Show Hide" Width="Auto" Click="buttonOne_Click"/>
<ListBox x:Name="aListBox">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="blockOne" Grid.Column="0" Text="Raw "/>
<TextBlock x:Name="blockTwo" Grid.Column="1" Text="{Binding aValue}" Visibility="{Binding Path=visControl.VisibleState, BindsDirectlyToSource=True}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
public partial class MainPage : PhoneApplicationPage
{
private List<myClass> listOfClasses = new List<myClass>();
// Constructor
public MainPage()
{
myClass classA = new myClass("one");
myClass classB = new myClass("two");
myClass classC = new myClass("three");
listOfClasses.Add(classA);
listOfClasses.Add(classB);
listOfClasses.Add(classC);
InitializeComponent();
aListBox.ItemsSource = listOfClasses;
}
private void buttonOne_Click(object sender, RoutedEventArgs e)
{
foreach (myClass cl in listOfClasses)
if (cl.SwitchVisible)
cl.SwitchVisible = false;
else
cl.SwitchVisible = true;
}
}
public class myClass
{
private string _aValue;
private bool _switchVisible;
public bool SwitchVisible { get { return _switchVisible; } set { _switchVisible = value; visControl.changeVisibility(_switchVisible); } }
public string aValue { get { return _aValue; } }
public controlProperties visControl;
public myClass(string invalue)
{
visControl = new controlProperties();
visControl.VisibleState = Visibility.Visible;
_aValue = invalue;
}
}
public class controlProperties
{
private Visibility _visibility;
public Visibility VisibleState { get { return _visibility; } set { _visibility = value; } }
public void changeVisibility(bool isVisible)
{
if (isVisible)
_visibility = Visibility.Visible;
else
_visibility = Visibility.Collapsed;
}
}
Any ideas if this is a pathing issue or a binding problem?
If you want the control to be automatically updated when you change the value of the property, your class must implement the INotifyPropertyChanged interface.
For instance:
public class controlProperties : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private Visibility _visibility;
public Visibility VisibleState
{
get
{
return _visibility;
}
set
{
_visibility = value;
this.NotifyPropertyChanged("VisibleState");
}
}
public void changeVisibility(bool isVisible)
{
if (isVisible)
this.VisibleState = Visibility.Visible;
else
this.VisibleState = Visibility.Collapsed;
}
private void NotifyPropertyChanged(string propertyName)
{
var eventHandler = this.PropertyChanged;
if (eventHandler != null)
{
eventHandler(sender, new PropertyChangedEventArgs(propertyName));
}
}
}
I have a UserControl (AgreementDetails) in WPF with the following DependencyProperty and function:
// UserControl AgreementDetails
public int AgreementID
{
get { return Convert.ToInt32(GetValue(AgreementIDProperty)); }
set { SetValue(AgreementIDProperty, value); }
}
public static readonly DependencyProperty AgreementIDProperty = DependencyProperty.Register("AgreementID", typeof(int), typeof(UC1001_AgreementDetails_View), new PropertyMetadata(null));
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
int id = AgreementID;
if (id > 0)
{
GetData();
SetBindingContext();
this.Visibility = System.Windows.Visibility.Visible;
}
else
{
this.Visibility = System.Windows.Visibility.Collapsed;
}
}
private void GetData()
{
ConsultantServiceClient client = new ConsultantServiceClient();
_contract = new UC1001_ActiveAgreementContract();
_contract = client.GetAgreementDetailsByAgreementID(AgreementID);
}
private void SetBindingContext()
{
this.DataContext = _contract;
}
I use this UserControl to show as a tooltip in another UserControl (Dashboard) where I set the AgreementID property:
// Dashboard
<Setter Property="DataGridCell.ToolTip">
<Setter.Value>
<my:UC1001_AgreementDetails_View Background="#FFF" Opacity="0.88" AgreementID="{Binding Months[9].AgreementID}"/>
</Setter.Value>
</Setter>
In AgreementDetails, I use the AgreementID to get some data from the database to show in the UserControl. The first time I do this, everything goes smooth. But when I set the incoming WCF DataContract as the datacontext in AgreementDetails, the AgreementID property resets to 0, so the second call will not work because obviously I do not have an agreement with AgreementID = 0. I checked and the AgreementID resets in the SetBindingContext(); method after the DataContext is set.
How can I make it so the AgreementID property will not reset after I set a new dataContext in AgreementDetails??
More information can be provided if wanted.
EDIT: I now have the following code:
// Dependency properties
public int AgreementID
{
get { return (int)GetValue(AgreementIDProperty); }
set { SetValue(AgreementIDProperty, value); }
}
public UC1001_ActiveAgreementContract AgreementDetailsContract
{
get { return (UC1001_ActiveAgreementContract)GetValue(AgreementDetailsContractProperty); }
set { SetValue(AgreementDetailsContractProperty, value); }
}
public static readonly DependencyProperty AgreementIDProperty = DependencyProperty.Register("AgreementID", typeof(int), typeof(UC1001_AgreementDetails_View), new PropertyMetadata(null));
public static readonly DependencyProperty AgreementDetailsContractProperty = DependencyProperty.Register("AgreementDetailsContract", typeof(UC1001_ActiveAgreementContract), typeof(UC1001_AgreementDetails_View), new PropertyMetadata(null));
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
int id = AgreementID;
if (id > 0)
{
GetData();
SetBindingContext();
this.Visibility = System.Windows.Visibility.Visible;
}
else
{
this.Visibility = System.Windows.Visibility.Collapsed;
}
}
private void GetData()
{
ConsultantServiceClient client = new ConsultantServiceClient();
AgreementDetailsContract = client.GetAgreementDetailsByAgreementID(AgreementID);
}
private void SetBindingContext()
{
this.DataContext = AgreementDetailsContract;
}
I still have the the problem that the AgreementID resets to 0 after the DataContext is set.
Also when I use the following statement to bind, I get an empty label:
<Label Content="{Binding RelativeSource={RelativeSource Self}, Path=AgreementDetailsContract.EndClientName}" />
SOLVED:
I removed the SetDataBinding() method so the Binding doesn't reset my DependencyProperty, and for the Binding of my labels I used the following Binding (instead of RelativeSource Self):
<Label Content="{Binding ElementName=AgreementDetails, Path=AgreementDetailsContract.EndClientName}" Grid.Column="1" Grid.Row="1" Height="28" HorizontalAlignment="Left" Margin="11,0,0,0" Name="_labelEindklant" VerticalAlignment="Top" />
ElementName=AgreementDetails is the name of my UserControl. Strange enough with {RelativeSource Self} it didn't work...
When you set the datacontext in your Usercontrol, you are actually resetting the data context in the parent control too (Dashboard). It's the same context. Because of this your Agreement ID is no longer in the context and so gets reset.
Edit: Actually I didn't word that very well. You're not affecting the data context in Dashboard, but you ARE affecting the data context used by the AgreementId binding declared in that control. The binding is declared in the Dashboard control, but the binding is actually looking in the data context of the child control, which you are resetting.
See my similar question here:
Setting DataContext within UserControl is affecting bindings in parent
EDIT: Here is what I mean:
// UserControl AgreementDetails
public int AgreementID
{
get { return Convert.ToInt32(GetValue(AgreementIDProperty)); }
set { SetValue(AgreementIDProperty, value); }
}
//The new property to bind to instead of DataContext
public UC1001_ActiveAgreementContract Agreement
{
get { return (UC1001_ActiveAgreementContract)GetValue(AgreementProperty); }
private set { SetValue(AgreementProperty, value); }
}
public static readonly DependencyProperty AgreementIDProperty = DependencyProperty.Register("AgreementID", typeof(int), typeof(UC1001_AgreementDetails_View), new PropertyMetadata(null));
//should really be readonly dependency property
public static readonly DependencyProperty AgreementProperty = DependencyProperty.Register("Agreement", typeof(UC1001_ActiveAgreementContract), typeof(UC1001_AgreementDetails_View), new PropertyMetadata(null));**
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
int id = AgreementID;
if (id > 0)
{
GetData();
SetBindingContext();
this.Visibility = System.Windows.Visibility.Visible;
}
else
{
this.Visibility = System.Windows.Visibility.Collapsed;
}
}
private void GetData()
{
ConsultantServiceClient client = new ConsultantServiceClient();
_contract = new UC1001_ActiveAgreementContract();
_contract = client.GetAgreementDetailsByAgreementID(AgreementID);
}
private void SetBindingContext()
{
this.Agreement = _contract;
}
Then in your AgreementDetails.xaml, you probably have something like:
<!-- Bound to property in DataContext -->
<TextBlock Text={Binding SomeContractProperty} />
which binding needs to change to:
<!-- Bound to new property on UC1001_AgreementDetails_View (UserControl) -->
<TextBlock Text={Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UC1001_AgreementDetails_View}}, Path=Agreement.SomeContractProperty} />