WPF binding xaml UI property to dynamic instance control property - c#

I have a custom media player object that I create in code behind from a user control. There can be 1 to 4 of these at any time, but i want to bind the volume and the mute property of only one to a xaml control EG.
The control is:
MediaControlplayer vcMediaPlayerMaster = new MediaControlplayer();
In this case the mute option to the ischecked state of the control does not work. How can I hook the binding up to the properties of the control when it is instantiated in code behind ?
xaml is like this. The variable vcMediaPlayerMaster is a global variable in code behind. When i instantiated it i assumed its declaration as a global predefined variable would allow the xaml below to bind to it, but it seems not to be the case.
<ToggleButton x:Name="btnAudioToggle" ToolTip="Audio Mute/Unmute"
Click="BtnAudioToggle_OnClick" IsChecked="{Binding Mode =TwoWay,
ElementName=vcMediaPlayerMaster, Path=Mute}" BorderBrush="LightBlue"
Width="32" Height="32" Margin="0,5,10,10" Background="{StaticResource
IbAudio}" Style="{DynamicResource ToggleButtonStyle1}" > </ToggleButton>
I thought perhaps creating a binding in code behind may be the way to go, but i cant seem to find a simple example that explains the code behind process to do that to fit my case.

You could create a helper class to hold the currently active MediaPlayer.
As a simple example:
public class MediaPlayerHelper : INotifyPropertyChanged
{
private MediaControlplayer currentPlayer;
public static MediaPlayerHelper Instance { get; } = new MediaPlayerHelper();
public MediaControlplayer CurrentPlayer
{
get => this.currentPlayer;
set { /* Implement a setter with INotifyPropertyChanged */ }
}
// Implement INotifyPropertyChanged here
}
The binding to this would look like the following
<Slider Value="{Binding Volume, Source={x:Static helper:MediaPlayerHelper.Instance}}"/>
Don't forget to include the namespace in the opening tag of your class in XAML:
xmlns:helper="clr-namespace:SomeNamespace.Helper"
Now you just have to change the currently used MediaPlayer whenever it changes:
MediaPlayerHelper.Instance.CurrentPlayer = newCurrentPlayer;

Ok i finally got it to work. Applied the binding in code behind fully.
I was able to bind the property i wanted to the ischecked property of a button to toggle the bool property of the mediaplayer object
MediaControlplayer vcMediaPlayerMaster = new MediaControlplayer();
Binding myMuteBinding = new Binding("Mute");
myMuteBinding.Source = vcMediaPlayerMaster;
myMuteBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
myMuteBinding.Mode = BindingMode.TwoWay;
btnAudioToggle.SetBinding(SimpleButton.IsCheckedProperty, myMuteBinding);
So this worked fine for me and i used the same principle to bind other properties.

Related

C# referencing a Grid in WPF to change properties

Hello im new to making apps with WPF and XAML in Visual Studio. So I have a grid I want to change its properties in the code.
My Grid's properties:
<Grid HorizontalAlignment="Left"
Height="603"
Margin="0,51,0,0"
x:Name="usersPan"
VerticalAlignment="Top"
Width="1286">
How I have been trying to change its properties
this.usersPan.SetValue(Grid.WidthProperty, PAN_SIZE);
usersPan.SetValue(Grid.WidthProperty, PAN_SIZE);
usersPan.Width = 0;
usersPan.Visibility = Visibility.Collapsed;
When I try to do that^ it says null reference for userPan
Thanks
Noooooooo, Don't ever do that. Make a ViewModel that is bound to the Grid's Width property, and then just change the value.
My suspicion is that you do not need this at all. Have a look into containers, and how to position them.
In all of this years, there have been rare occasions I needed to do that and I suspect you do not need to. Tell me what you are doing.
EDIT:
You have a VM which needs to implement the NotifyPropertyChanged interface (I won't do that here, there are plenty of examples on hoew to do that)
public class MainVM
{
public ObservableCollection<TabVM> TabsVms {get;set;}
public int SelectedIndex {get;set}
}
bound to the control
<TabControl DataContext={TabsVMs} SelectedIndex="{Binding SelectedIndex}">
...
</TabControl>
And in runtime you create a couple of Tabs
var TabsVMs = new ObservableCollection<TabVM>();
TabsVMs.add(new TabVM());
TabsVMs.add(new TabVM());
TabsVMs.add(new TabVM());
Then in runtime you change the value of the index.
MainVm.SelectedIndex = 1
and the the coresponding tab will become selected.
EDIT:
I can also recommend you to use Fody for the MVVM notification.
Also, when it comes to bindings, I can recommend you to use WPF inspector. a handy little tool
The best way to write WPF programs is to use the MVVM (Model-View-View Model) design pattern. There are two (2) ideas behind MVVM:
Write as little code as possible in the view's code-behind and put all of the logic in the View Model object, using WPF's data binding feature to connect the properties of the View Model object to the view's controls.
Separate the logic from the display so you can replace the view with some other construct without having to change the logic.
MVVM is a huge topic on its own. There are lots of articles about it, and frameworks that you can use to build your program. Check out MVVM Light, for example.
Don't know exactly why Grid is invisible in code-behind, but You can access it's properties using events (but don't think it is perfect solution).
For example add to your grid event Loaded
<Grid HorizontalAlignment="Left"
Height="603"
Margin="0,51,0,0"
x:Name="usersPan"
VerticalAlignment="Top"
Width="1286"
Loaded="FrameworkElement_OnLoaded">
and then from code-behind you can access grid in next way:
private void FrameworkElement_OnLoaded(object sender, RoutedEventArgs e)
{
var grid = sender as Grid;
if (grid != null)
{
grid.Width = 0;
}
}
Better solution :
Add some boolean property to your ViewModel like public bool IsGridVisible{get;set;}
And bind it to your Grid
<Grid HorizontalAlignment="Left"
Height="603"
Margin="0,51,0,0"
x:Name="usersPan"
VerticalAlignment="Top"
Width="1286"
Visibility="{Binding Path=IsGridVisible, Converter={StaticResource BoolToVis}">
where BoolToVis is converter which converts true to Visible and false to Hidden. You can define it in App.xaml like :
<BooleanToVisibilityConverter x:Key="BoolToVis" />
I was able to do something like this so I can change properties outside of an event.
private Grid userGrid;
private void onUserGridLoaded(object sender, RoutedEventArgs e)
{
userGrid = sender as Grid;
}

WPF control properties set in 'code behind', after that binding is ignored

I'm really new in WPF. I tried to set a default value for a control-property in code and want to overwrite the property by data binding, when the datacontext (VM) is available. But the databinding is not working in this case.
Example:
code behind:
public partial class MyViewControl : UserControl
{
public MyViewControl()
{
InitializeComponent();
// it works if I remove this line
panelControl.Visibility = Visibility.Hidden;
}
}
xaml usercontrol:
<DockPanel Name="panelControl" Visibility="{Binding
MyViewModelProperty_IsVisible_ConvertedToVisibility}">
xaml mainwindow:
<my:MyViewControl DataContext="{Binding ElementName=lbListBox,
Path=SelectedItem}"/>
Actually the FallbackValue parameter works for this scenario, but I want to know the technical reason, why the control property cannot be bound after it was set by code?
Xaml is processed during InitializeComponent(), so this is what is happening:
InitializeComponent(); // binding is set
panelControl.Visibility = Visibility.Hidden; // binding is removed (value is set)
You can restore binding
InitializeComponent();
panelControl.Visibility = Visibility.Hidden;
BindingOperations.SetBinding(panelControl, Control.VisibilityProperty,
new Binding()
{
Path = new PropertyPath(nameof(ViewModel.MyViewModelProperty_IsVisible_ConvertedToVisibility)),
Source = viewModelInstance, // this.DataContext ?
});
And it will work after. But it's not really clear why do you want to overwrite binding in first place.
A simple way to prevent a binding being cleared when changing bound property value in code, is to use TwoWay binding mode:
<DockPanel Name="panelControl"
Visibility="{Binding MyViewModelProperty_IsVisible_ConvertedToVisibility,
Mode=TwoWay}">
I found this out the hard way, of course, lol.
Actually, using TwoWay mode does make sense if you have a reason for modifying a control's property directly (as opposed to modifying the bound property) - you would then want the bound property to reflect the change too.
By the way, instead of binding to a property of type Visibility it is better to use bind to a boolean and use a converter like BooleanToVisibilityConverter as it decouples ViewModel from View even better:
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</Window.Resources>
...
<DockPanel Name="panelControl"
Visibility="{Binding MyViewModelProperty_IsVisible,
Converter={StaticResource BooleanToVisibilityConverter},
Mode=TwoWay}">
Yeah, I know this is an old question and has an accepted answer but I found no other answer offering this solution directly.

WPF Game editor - Updating properties in UI/code

I'm trying to create a game editor in C#/WPF. The editor consists of a user control that shows the scene (rendered in OpenGL using SharpGL) as well as many controls for editing the current scene and objects. Objects consist of components whose properties can be edited in the editor (kind of like in Unity game engine). I already have a "component editor" view which uses reflection to find all properties on the component and creates a property editor (for example, a slider) per each property. However, I'm not sure how to bind the properties between UI and code.
The problem is, I want these properties to be updated in the UI when they change in code, as well as updated in code when they're changed in the UI. If I want to bind the editor controls (such as a slider that changes a property) to the component properties, they would have to implement NotifyPropertyChanged, which would be quite cumbersome. I guess the other way is doing dirty-checking, but I'm not sure if that's a good idea either.
Can anybody give me pointers on how this property updating between UI/Code should be handled? I want it to work pretty much like it does in Unity, where you don't need to write anything extra into your component class to make properties editable.
EDIT: To make more clear what I'm trying to achieve and already have, here is a part of the "component editor" user control. It's datacontext is a Component instance (model). PropertiesConverter returns it's properties (through component.GetType().GetProperties()). ComponentPropertyTemplateSelector decides on the property editor user control (for example, for a double property it would select a "number editor" that has a textbox for editing the value). The problem that I'm interested in solving is how to two-way bind a Component's property to an editor control.
<ItemsControl x:Name="ComponentProperties" Grid.Row="1" ItemTemplateSelector="{StaticResource ComponentPropertyTemplateSelector}">
<ItemsControl.ItemsSource>
<Binding Converter="{StaticResource PropertiesConverter}"/>
</ItemsControl.ItemsSource>
</ItemsControl>
I would say you probably want to follow the MVVM pattern which does use the INotifyPropertyChanged interface. If you do a Google search on MVVM there are some good articles that come up right away. There are also some existing tools out there already to help get you started. From what you describe in your question the MVVM pattern essentially works that way. It decouples the UI and the code but still maintains that connection. The real quick version is that you implement the INotifyPropertyChanged on a class and then you set an instance of that class to the DataContext of the control you want to setup the binding for. Probably easier to see an example:
Xaml:
<StackPanel>
<Slider Value="{Binding SliderValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Text="{Binding MyText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Text="{Binding MyText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Slider Value="{Binding SliderValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
I created a view model base to save on some code writing:
class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
An example view model class:
class MyViewModel : ViewModelBase
{
private int sliderValue;
private string myText;
public int SliderValue
{
get { return this.sliderValue; }
set
{
this.sliderValue = value;
this.OnPropertyChanged();
}
}
public string MyText
{
get { return this.myText; }
set
{
this.myText = value;
this.OnPropertyChanged();
}
}
}
How to hook up the binding (in this case the code behind of the control):
public MainWindow()
{
InitializeComponent();
this.DataContext = new MyViewModel();
}
As you can see there is some work involved in setting up the view models and xaml. Compared to other solutions I think this is pretty good as far as the amount of "work" you have to put in. I don't know if there is any way to get around it though and have it work like "magic". It might be worth checking into what MVVM tools exist, there may be stuff out there that can make things even more simple.
You can add IPropertyChangeNotification support automatically using either Castle Dynamic Proxy (which wraps the classes in a proxy) or Fody (which modifies the IL in a post-build step).

How can I bind a xaml property to a static variable in another class?

I have this xaml file in which I try to bind a Text-block Background to a static variable in another class, how can I achieve this ?
I know this might be silly but I just moved from Win-forms and feeling a little bit lost.
here is what I mean:
<TextBlock Text="some text"
TextWrapping="WrapWithOverflow"
Background="{Binding Path=SomeVariable}" />
First of all you can't bind to variable. You can bind only to properties from XAML.
For binding to static property you can do in this way (say you want to bind Text property of TextBlock) -
<TextBlock Text="{Binding Source={x:Static local:YourClassName.PropertyName}}"/>
where local is namespace where your class resides which you need to declare above in xaml file like this -
xmlns:local="clr-namespace:YourNameSpace"
You can't actually bind to a static property (INotifyPropertyChanged makes sense on instances only), so this should be enough...
{x:Static my:MyTestStaticClass.MyProperty}
or e.g.
<TextBox Text="{x:Static my:MyTestStaticClass.MyProperty}" Width="500" Height="100" />
make sure you include the namespace - i.e. define the my in the XAML like xmlns:my="clr-namespace:MyNamespace"
EDIT: binding from code
(There're some mixed answers on this part so I thought it made sense to expand, have it in one place)
OneTime binding:
You could just use textBlock.Text = MyStaticClass.Left (just careful where you place that, post-init)
TwoWay (or OneWayToSource) binding:
Binding binding = new Binding();
//binding.Source = typeof(MyStaticClass);
// System.InvalidOperationException: 'Binding.StaticSource cannot be set while using Binding.Source.'
binding.Path = new PropertyPath(typeof(MyStaticClass).GetProperty(nameof(MyStaticClass.Left)));
binding.Mode = BindingMode.TwoWay;
binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
this.SetBinding(Window.LeftProperty, binding);
...of course if you're setting Binding from the code remove any bindings in XAML.
OneWay (property changes from the source):
And if you'd need to update the target (i.e. the control's property, Window.Left in this case) on the source property changes, that can't be achieved with the static class (as per my comment above, you'd need the INotifyPropertyChanged implemented, so you could just use a wrapper class, implement INotifyPropertyChanged and wire that to a static property of your interest (providing you know how to track you static property's changes, i.e. this is more of a 'design' issue from this point on, I'd suggest redesigning and putting it all within one 'non-static' class).
You can use the newer x:Bind to do this simply using:
<TextBlock Text="{x:Bind YourClassName.PropertyName}"/>

Outside a datatemplate On..PropertyValueChanged doesn't run

I have a custom control which uses On(propertyname)ValueChanged to read items from a dictionary and set up the parameters for that control.
I would also like to use that control as a stand alone and not just a databound control.
So how come OnPropertyValueChanged only works in a dataset?
Do Dependency properties only work from the xaml, does that mean i will have to bind from properties in the container class? (may have answered my own question)
in my mainpage.xaml
<local:spriteToggleButton x:Name="testButton" HorizontalAlignment="Center" Text="{Binding testString, ElementName=mainPage}" Correct="true" Margin="93,561,93,63" Grid.Row="1" Sprites="{Binding testSprites, ElementName=mainPage}" />
in mainpage.xaml.cs
testSprites.Add("idle", idlesprite); // a dictionary of a custom sprite object
testSprites.Add("highlighted", highlightedsprite);
testSprites.Add("selected", selectedsprite);
testString = "this is a test"; // this property is picked up by the binding.
when i add sprites from the binding it runs the dependency property changed callback but the properties inside the spriteToggleButton class are not updating when the control is used standalone
this is my dependency property changed callback
private static void OnSpritesPropertyValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as spriteToggleButton;
var sprites = e.NewValue as Dictionary<string, Quiz.Sprite>;
control.idleSprite = sprites["idle"];
control.selectedSprite = sprites["selected"];
control.highlightedSprite = sprites["highlighted"];
}
this is inside my spriteToggleButton
<local:spriteView x:Name="Idle" Width="294" Height="57" HorizontalAlignment="Center" Sprite="{Binding idleSprite, ElementName=toggleSpriteControl}" />
...
Sprite is also a dependency property in that control
I very much suspect that you haven't implemented idleSprite, selectedSprite and highlightedSprite of the spriteToggleButton class as dependency properties. Do that and it should start working.
For what its worth it appears you are implementing spriteToggleButton using a UserControl, I would instead derive from ToggleButton and replace the default template.

Categories

Resources