I am trying to make a progress bar that updates when a property value changes
I have followed other questions but i don't know what is wrong with it.
This is XAML code:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1" x:Class="WpfApplication1.MainWindow"
Title="MainWindow">
<Grid Margin="0,0,-8,1">
<ProgressBar Value="{Binding Progreso, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}}" Margin="105,95,207,350"/>
<Button Content="Button" Click="Button_Click" Margin="218,232,333,217"/>
</Grid>
</Window>
it is basically a progress bar with the binding and a button with a listener that increases Progreso by 10
this is the C# code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string sProp)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(sProp));
}
}
float progreso = 10;
public float Progreso
{
get
{
return progreso;
}
set
{
progreso = value;
NotifyPropertyChanged("Progreso");
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.Progreso = this.Progreso + 10;
}
}
I tried to keep it simple but I couldn't get it to work, any help with this would be appreciated.
edit: I've also tried UpdateSourceTrigger=PropertyChanged and that didn't work either
Your problem is you missed the declaration of INotifyPropertyChanged interface implementation like this:
public partial class MainWindow : Window, INotifyPropertyChanged {
//....
}
NOTE: Using RelativeSource works OK, I tested this. Using DataContext is just an implicit way to set the Source although it's a convenient and a recommended way.
UPDATE
About using DataContext:
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
<ProgressBar Value="{Binding Progreso}" Margin="105,95,207,350"/>
AncestorType does not seem to work property. So you have two options:
Set Window Name and find DataContext by ElementName
Set DataContext to be this in code behind and remove RelativeSource part
Related
I'm trying to simplify some code by putting the ViewModel models into the code behind and binding the DataContext as "this", but it seems to work differently, in the following example:
Why is it when the button is clicked, the TextBlock bound to "Message" does not change, even though OnPropertyChanged("Message") is called?
XAML:
<Window x:Class="TestSimple223.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<StackPanel HorizontalAlignment="Left">
<Button Content="Button"
Click="button1_Click" />
<TextBlock
Text="{Binding Path=Message, Mode=TwoWay}"/>
<TextBlock
x:Name="Message2"/>
</StackPanel>
</Window>
Code Behind:
using System.Windows;
using System.ComponentModel;
namespace TestSimple223
{
public partial class Window1 : Window
{
#region ViewModelProperty: Message
private string _message;
public string Message
{
get
{
return _message;
}
set
{
_message = value;
OnPropertyChanged("Message");
}
}
#endregion
public Window1()
{
InitializeComponent();
DataContext = this;
Message = "original message";
Message2.Text = "original message2";
}
private void button1_Click(object sender, RoutedEventArgs e)
{
Message = "button was clicked, message changed";
Message2.Text = "button was click, message2 changed";
}
#region INotify
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
}
You haven't marked your class as being available for property change notification. Change the heading to
public partial class Window1 : Window, INotifyPropertyChanged
Just because you implement the methods doesn't mean that WPF knows that a class supports change notification - you need to tell it by marking it with INotifyPropertyChanged. This way, the binding mechanism can identify your class as a potential update target.
Could you tell me how to in pure MVVM way call (I mean open/show) child window from parent window. Let's say I have two Views:
MainWindow.cs (MainWindow.xaml) - parent window (DataContext = new MainWindowViewModel())
Window.cs (Window.xaml) - child window (DataContext = new WindowViewModel())
And corresponding ViewModel classes:
MainWindowViewModel.cs
WindowViewModel.cs
I would like my window to be opened after button click (button that is on the MainWindow view). Because of that I have defined command binding in MainWindow.xaml:
<Button x:Name="buttonOpenWindow" Content="Open window..." Width="100" Height="20" Command="{Binding OpenWindowCmd}"/>
And MainWindowViewModel.cs piece:
public ICommand OpenWindowCmd { get; set; }
public MainWindowViewModel()
{
OpenWindowCmd = new RelayCommand(o => OpenWindow());
}
private void OpenWindow()
{
// What to put here?
}
In Window.xaml I added something like that:
<Window x:Class="Namespace.View.Window"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:vm="clr-namespace:Namespace.ViewModel"
Title="Title" Height="300" Width="325" Visibility="{Binding IsWindowVisible, Converter={StaticResource BooleanToVisibilityConverter}}">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
(...)
And the WindowViewModel.cs:
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Namespace.Annotations;
namespace Namespace.ViewModel
{
public class WindowViewModel : INotifyPropertyChanged
{
private bool _isWindowVisible;
public bool IsWindowVisible
{
get { return _isWindowVisible; }
set
{
_isWindowVisible = value;
OnPropertyChanged(nameof(IsWindowVisible));
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I am not sure what to do next and if that approach is correct. I found some services implementations in the forum, but I thought of using just Visibility property instead (but not sure if it is possible). I need to somehow change the IsWindowVisible in one of the view models I suppose. Could anyone suggest how to gently handle such sub window opening?
If I understood well, you need something like this:
private void OpenWindow()
{
WindowViewModel wvm = new WindowViewModel();
Window win = new Window()
{
DataContext = wvm;
};
win.Show();
}
If you don't like this solution then try the one from the comments with IWindowService.
In any case it makes no sense to use a Visibility property.
This is a mostly out of curiosity question and to hopefully help me better understand binding, XAML, and extension syntax.
So I simply want to change the binding source from the MainWindow to an object I have instantiated in MainWindow.
Here is my C# code:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
favclass myfavclass = new favclass();
InitializeComponent();
this.DataContext = this;
}
string _myString = "hello";
public string MyString
{
get { return _myString; }
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(
this, new PropertyChangedEventArgs(propName));
}
}
public class favclass : INotifyPropertyChanged
{
int _myint = 34;
public int MyInt
{
get { return _myint; }
set { _myint = value; }
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(
this, new PropertyChangedEventArgs(propName));
}
}
}
and my XAML
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" >
<Grid>
<TextBlock Height="50" Width="50" Text="{Binding MyString}"/>
<TextBlock Height="50" Width="48" Margin="200,100,100,100"
Text="{Binding Source=myfavclass, Path=MyInt}"/>
</Grid>
</Window>
So as you can see I want first display the MyString property from main window.
Then I want to display the MyInt from the myfavclass object. But of course MyInt doesn't appear. I've tried every variation I can think of.
What XAML am I missing? Why doesn't the XAML I have work?
Thanks
Source=myfavclass this is wrong. Source can be only assigned directly using element syntax like this:
<Binding>
<Binding.Source>
<!-- value here -->
</Binding.Source>
</Binding>
Or you can use StaticResource or DynamicResoure or some custom MarkupExtension like this:
Text="{Binding Source={StaticResource someKey}, Path=MyInt}"
Or use the new feature {x:Reference} to get reference directly to some named element inside XAML:
Text="{Binding Source={x:Reference someName}, Path=MyInt}"
Moreover the myfavclass is declared as local variable inside your code behind. There is no way it can be used (referenced) inside XAML code.
You're doing something called multiple viewmodels. If so you should provide multiple DataContext for your controls. I prefer to using nested viewmodels. To implement this, you can try modifying the MainWindow like this:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
_myfavclass = new favclass();
InitializeComponent();
this.DataContext = this;
}
private readonly favclass _myfavclass;
//we will use this property inside XAML code
public favclass MyFavClass {
get {
return _myfavclass;
}
}
}
Now in XAML code, you can bind the Text to the MyFavClass.MyInt, note that the DataContext is implicitly the source for the Binding, so you just need to specify the Path:
<TextBlock Height="50" Width="48" Margin="200,100,100,100"
Text="{Binding Path=MyFavClass.MyInt}"/>
Your MyInt is not properly implemented using INotifyPropertyChanged (but I hope you already know that).
favclass myfavclass = new favclass(); should be declared out of the init method,or you won't get this.myfavclass instance
I'm trying to make Avalon MVVM compatible in my WPF application. From googling, I found out that AvalonEdit is not MVVM friendly and I need to export the state of AvalonEdit by making a class derived from TextEditor then adding the necessary dependency properties. I'm afraid that I'm quite lost in Herr Grunwald's answer here:
If you really need to export the state of the editor using MVVM, then I suggest you create a class deriving from TextEditor which adds the necessary dependency properties and synchronizes them with the actual properties in AvalonEdit.
Does anyone have an example or have good suggestions on how to achieve this?
Herr Grunwald is talking about wrapping the TextEditor properties with dependency properties, so that you can bind to them. The basic idea is like this (using the CaretOffset property for example):
Modified TextEditor class
public class MvvmTextEditor : TextEditor, INotifyPropertyChanged
{
public static DependencyProperty CaretOffsetProperty =
DependencyProperty.Register("CaretOffset", typeof(int), typeof(MvvmTextEditor),
// binding changed callback: set value of underlying property
new PropertyMetadata((obj, args) =>
{
MvvmTextEditor target = (MvvmTextEditor)obj;
target.CaretOffset = (int)args.NewValue;
})
);
public new string Text
{
get { return base.Text; }
set { base.Text = value; }
}
public new int CaretOffset
{
get { return base.CaretOffset; }
set { base.CaretOffset = value; }
}
public int Length { get { return base.Text.Length; } }
protected override void OnTextChanged(EventArgs e)
{
RaisePropertyChanged("Length");
base.OnTextChanged(e);
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
Now that the CaretOffset has been wrapped in a DependencyProperty, you can bind it to a property, say Offset in your View Model. For illustration, bind a Slider control's value to the same View Model property Offset, and see that when you move the Slider, the Avalon editor's cursor position gets updated:
Test XAML
<Window x:Class="AvalonDemo.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:avalonEdit="http://icsharpcode.net/sharpdevelop/avalonedit"
xmlns:avalonExt="clr-namespace:WpfTest.AvalonExt"
DataContext="{Binding RelativeSource={RelativeSource Self},Path=ViewModel}">
<StackPanel>
<avalonExt:MvvmTextEditor Text="Hello World" CaretOffset="{Binding Offset}" x:Name="editor" />
<Slider Minimum="0" Maximum="{Binding ElementName=editor,Path=Length,Mode=OneWay}"
Value="{Binding Offset}" />
<TextBlock Text="{Binding Path=Offset,StringFormat='Caret Position is {0}'}" />
<TextBlock Text="{Binding Path=Length,ElementName=editor,StringFormat='Length is {0}'}" />
</StackPanel>
</Window>
Test Code-behind
namespace AvalonDemo
{
public partial class TestWindow : Window
{
public AvalonTestModel ViewModel { get; set; }
public TestWindow()
{
ViewModel = new AvalonTestModel();
InitializeComponent();
}
}
}
Test View Model
public class AvalonTestModel : INotifyPropertyChanged
{
private int _offset;
public int Offset
{
get { return _offset; }
set
{
_offset = value;
RaisePropertyChanged("Offset");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
You can use the Document property from the editor and bind it to a property of your ViewModel.
Here is the code for the view :
<Window x:Class="AvalonEditIntegration.UI.View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:AvalonEdit="clr-namespace:ICSharpCode.AvalonEdit;assembly=ICSharpCode.AvalonEdit"
Title="Window1"
WindowStartupLocation="CenterScreen"
Width="500"
Height="500">
<DockPanel>
<Button Content="Show code"
Command="{Binding ShowCode}"
Height="50"
DockPanel.Dock="Bottom" />
<AvalonEdit:TextEditor ShowLineNumbers="True"
Document="{Binding Path=Document}"
FontFamily="Consolas"
FontSize="10pt" />
</DockPanel>
</Window>
And the code for the ViewModel :
namespace AvalonEditIntegration.UI
{
using System.Windows;
using System.Windows.Input;
using ICSharpCode.AvalonEdit.Document;
public class ViewModel
{
public ViewModel()
{
ShowCode = new DelegatingCommand(Show);
Document = new TextDocument();
}
public ICommand ShowCode { get; private set; }
public TextDocument Document { get; set; }
private void Show()
{
MessageBox.Show(Document.Text);
}
}
}
source : blog nawrem.reverse
Not sure if this fits your needs, but I found a way to access all the "important" components of the TextEditor on a ViewModel while having it displayed on a View, still exploring the possibilities though.
What I did was instead of instantiating the TextEditor on the View and then binding the many properties that I will need, I created a Content Control and bound its content to a TextEditor instance that I create in the ViewModel.
View:
<ContentControl Content="{Binding AvalonEditor}" />
ViewModel:
using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Highlighting;
// ...
private TextEditor m_AvalonEditor = new TextEditor();
public TextEditor AvalonEditor => m_AvalonEditor;
Test code in the ViewModel (works!)
// tests with the main component
m_AvalonEditor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("XML");
m_AvalonEditor.ShowLineNumbers = true;
m_AvalonEditor.Load(#"C:\testfile.xml");
// test with Options
m_AvalonEditor.Options.HighlightCurrentLine = true;
// test with Text Area
m_AvalonEditor.TextArea.Opacity = 0.5;
// test with Document
m_AvalonEditor.Document.Text += "bla";
At the moment I am still deciding exactly what I need my application to configure/do with the textEditor but from these tests it seems I can change any property from it while keeping a MVVM approach.
I want to dynamically change TextBlock text in my Class.
XAML-Code:
<TextBlock Name="Footer_text" Text=""/>
C#:
string footerMainMenuText = "Setting";
Binding set = new Binding("");
set.Mode = BindingMode.OneWay;
set.Source = footerMainMenuText;
Footer_text.DataContext = footerMainMenuText;
Footer_text.SetBinding(TextBlock.TextProperty, set);
I checked last line, and the Footer_text.Text is set correctly. ( Footer_text.Text="Setting"), but TextBlock in my application doesnt show "Setting". What is the problem here?
If you are binding - why not just do it in XAML instead? Looking at your code it's kind of pointless - you might as well just go
Footer_text.Text = "Setting";
You should ideally do it in XAML or at least provide something for it to bind to
<TextBlock Text="{Binding SomeProperty}" />
I'm not sure why you would bind a 'string' on it's own to anything...do you have an object which you need to bind to the text property?
Also using
Binding("")
What does that do? A blank path? Not sure what the binding target would be there... have you tried
Binding()
instead?
Edit:
Also the reason why your binding is not updating the control, is probably because you haven't bound to an object which implements INotifyPropertyChanged or a similar interface. The controls need to know when values have changed, so I'd imagine that binding to 'string' isn't giving the TextBlock the proper notification when it changes
Edit 2:
Here is a quick example of binding working:
My window class Window.cs:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel>
<TextBlock x:Name="txtName" Text="{Binding Name}"></TextBlock>
<Button Click="Button_Click">Click me 1</Button>
<Button Click="Button_Click_1">Click me 2</Button>
</StackPanel>
</Grid>
</Window>
The code behind in Window.xaml.cs
public partial class MainWindow : Window
{
SomeObjectClass obj = new SomeObjectClass();
public MainWindow()
{
InitializeComponent();
txtName.DataContext = obj;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
obj.Name = "Hello World";
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
obj.Name = "Goobye World";
}
}
The object to bind to (with INotifyPropertyChanged)
class SomeObjectClass : INotifyPropertyChanged
{
private string _name = "hello";
public string Name
{
get
{
return _name;
}
set
{
_name = value;
OnPropertyChanged("Name");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
Clicking the buttons changes SomeObject.Name, but it updates the textbox.