I am using WPF and trying to use binding to make buttons work, I am more at home in WinForms but have just started with WPF and MVVM and am a bit lost. I have some buttons already working, I used GalaSoft.MvvmLight.CommandWpf only because that was what was already in use.
I now have a PAUSE button (text on button = PAUSE) which when pressed will stop the sequence and the text on the button will change to RESUME. I have it bound and can stop the sequence and resume it OK.
In the XML I have
<Button Grid.Row="7" Grid.Column="0" Name="{Binding onPauseCommand}" Content="Pause" Height="37" Width="88" Command="{Binding onPauseCommand}" />
The name is what I think I need to bind to something and as you see at the moment it is just bound to onPauseCommand which you will rightly say is wrong. I agree it does nothing.
In the model view I have
public bool _pause;
public bool Pause
{
get => _pause;
set
{
if (_pause != value)
{
_pause = value;
}
}
}
public string _btn_pause_text;
public string Btn_Pause_Text
{
get => _btn_pause_text;
set
{
if (_btn_pause_text != value)
{
_btn_pause_text = value;
}
}
}
I also have the buttons bound command, which while it may not be the best implementation sets _pause correctly. I know this is not the place for the _btn_pause_text but this is just showing what I tried. If this was a Winform you woudl say something like btn_pause.Text = "Resume" or btn_pause.Text = "Pause".
//Used to Pause the Sequence
public ICommand onPauseCommand => new RelayCommand(onPause);
private void onPause()
{
if (_pause == false)
{
_pause = true; //set to pause sequence
_btn_pause_text = "Resume";
}
else
{
_pause = false;
_btn_pause_text = "Pause";
}
}
Any ideas how to change the buttons text please?
Related
I'm using Fluent Ribbon in a MVVM design pattern, with a View/ViewModel. My goal is to change the body according to which tab is selected. I could use either SelectedTabItem or SelectedTabIndex and lookup the corresponding view. However, neither fire when the tab selection changes. In the View I have...
<Fluent:Ribbon Grid.Row="0" SelectedTabItem="{Binding SelectedRibbonTab}" SelectedTabIndex="{Binding SelectedRibbonTabIndex}">
</Fluent:Ribbon>
<ContentControl Grid.Row="1" Content="{Binding RibbonTabContent}"/>
In the ViewModel I have...
// Used both Item and Index for troubleshoothing, but need only one or the other for implementation
private IDictionary<string, FrameworkElement> RibbonTabViews;
private List<FrameworkElement> RibbonTabViewsList;
public RibbonTabItem SelectedRibbonTab
{
get
{
return selectedRibbonTab;
}
set
{
Update(() => SelectedRibbonTab, ref selectedRibbonTab, value, false);
UpdateContentControl();
}
}
public int SelectedRibbonTabIndex
{
get
{
return selectedRibbonTabIndex;
}
set
{
Update(() => SelectedRibbonTabIndex, ref selectedRibbonTabIndex, value, false);
UpdateContentControl(value);
}
}
public FrameworkElement RibbonTabContent
{
get { return ribbonTabContent; }
set { Update(() => RibbonTabContent, ref ribbonTabContent, value, false); }
}
protected void UpdateContentControl()
{
RibbonTabContent = RibbonTabViews[SelectedRibbonTab.Header.ToString()];
}
protected void UpdateContentControl(int index)
{
RibbonTabContent = RibbonTabViewsList[index];
}
I know I don't need both bindings but for the sake of troubleshoot I have both Item and Index. I would think in the ViewModel SelectedRibbonTab and SelectedRibbonTabIndex would be called each time the tab changed. Unfortunately, that doesn't appear to be the case. I have breakpoints at the setters and getters for each and neither are hit when changing tabs. What am I missing? I've been using this approach for years with Microsoft Ribbon but for some reason these don't fire in Fluent Ribbon. Any help would be great, thank you.
You have to set the binding mode to TwoWay to get updated values in your ViewModel.
i have a comboxbox that while it is beign populated i want it replaced in the UI by a message saying it is being loaded.
i did this by using a textbox showing the message and giving both objects visibility bindings in the view model (IsShowAuthComboBox &LoadingAuthenticationMsg)
here's the XAML code
<ComboBox x:Name="ComboBoxAuthSource"
Grid.Row="3"
Style="{StaticResource ComboBoxStyle}"
SelectedItem ="{Binding SelectedAuthenticationSource,UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding AuthenticationSource,UpdateSourceTrigger=PropertyChanged}"
Visibility= "{Binding IsShowAuthComboBox, Converter={StaticResource BoolToVis}}" />
<TextBox x:Name="ComboBoxAuthCover"
Grid.Row="3" Grid.Column="{StaticResource TableColumn}"
Style="{StaticResource FieldBoxStyle }"
FontSize="12"
IsReadOnly="True"
Visibility="{Binding IsShowGettingAuthenticationMsg, Converter={StaticResource BoolToVis}}"
Text="{Binding LoadingAuthenticationMsg,UpdateSourceTrigger=PropertyChanged,Mode=OneWay,FallbackValue='Loading authentication sources...'}" />
And here's the viewModel
public bool IsShowAuthComboBox
{
set
{
if (_isShowAuthenticationComboBox != value)
{
_isShowAuthenticationComboBox = value;
OnPropertyChanged("IsShowAuthComboBox");
OnPropertyChanged("IsShowGettingAuthenticationMsg");
}
}
get =>_isShowAuthenticationComboBox;
}
public bool IsShowGettingAuthenticationMsg => !_isShowAuthenticationComboBox;
public virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
Log.Write(LogClass.General, LogLevel.Debug,
$"{propertyName} update triggerd",
_moduleName);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
this code is the first thing that happens in the relevant flow, but i will sometimes only see it at the very end of the execution and for only for an instant.
at other times it will work as expected.
what am i missing here?
EDIT :
this also accurs when validating the IP ,simpler code.
here's the code
public string SelectedServer
{
get => _selectedServer;
set
{
lock (_lockObj)
{
IsShowAuthComboBox = false;
if (!IsValideIp(value))
//some code
IsShowAuthComboBox = true;
}
}
bool IsValideIp(string ip)
{
//some code
//calls the server sync
return RemotingConfigurator.GetServerConfig(ip).isValid;
}
Your issue is that you are setting the IsShowAuthComboBox property and calling the IsValideIp synchronously on the same thread. And a single thread cannot both update the UI and query a database simultaneously.
What you should do is to call the IsValideIp on a background thread. I wouldn't do this in the setter of a property though, but rather in a command. You may want to read #Stephen Cleary's blog post on the subject.
this is what i ended up doing. moved the UI changes away from the data layer and into the viewModel (SetUiOnWait)
public string SelectedServer
{
get => _selectedServer;
set
{
//IsShowAuthComboBox = false;
SetUiOnWait(true);
Log.Write(LogClass.General, LogLevel.Debug,
$"Server changed from {_selectedServer} to {value} by user",
_moduleName);
_selectedServer = value;
OnPropertyChanged();
// OnPropertyChanged();
//workaround for when changing servers when a unique
//authentication source is selected causes the selected source to be null :\
if (AuthenticationSource.Any())
{
SelectedAuthenticationSource = AuthenticationSource[0];
}
Task.Factory.StartNew(() =>
{
LoginInfo.SelectedServer = _selectedServer;
}).ContinueWith((t) =>
{
if(t.Exception !=null)
{
ExceptionLog.Write(t.Exception.GetBaseException(),_moduleName);
}
RefreshAuthenticationProperties();
OnPropertyChanged("IsLimitedClinicalUse");
OnPropertyChanged("IsNotForClinicalUse");
SetUiOnWait(false);
});
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
dispatcher.Invoke((Action)(() =>
{
//PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}));
}
Task.Factory.StartNew() forces and logic to be executed on a new thread and for the UI changes to wait for it be completed.
and invoke within OnPropertyChange forces the event to be handled by the UI thread.
I have WPF MVVM application. There i have one user control with popup. When i click on one of the user control's button (Parent Bindings) i wish to show popup. (likewise close)
Command="{Binding Parent.ShowPopupCommand}"
<Popup Name="Popup1" IsEnabled="True"
IsOpen="{Binding DisplayHelper.IsOpenPopup, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" >
</Popup>
Instead of writing Dependency Property in user control i wrote separate view model with INotifyPropertyChanged interface. On login i am binding popup IsOpen property from Login.cs
RelayCommand _showPopupCommand;
RelayCommand _hidePopupCommand;
public ICommand ShowPopupCommand
{
get
{
if (_showPopupCommand == null)
{
_showPopupCommand = new RelayCommand(param => this.ShowPopup(), null);
}
return _showPopupCommand;
}
}
public ICommand HidePopupCommand
{
get
{
if (_hidePopupCommand == null)
{
_hidePopupCommand = new RelayCommand(param => this.HidePopup(), null);
}
return _hidePopupCommand;
}
}
private void HidePopup()
{
DisplayHelper ds = new DisplayHelper();
ds.IsOpenPopup = false;
}
private void ShowPopup()
{
DisplayHelper ds = new DisplayHelper();
ds.IsOpenPopup = true;
}
but popup is not showing on click.
Please help in this
Your problem is that you create new instances of DisplayHelper each time you run a command, but the View looks for a DisplayHelper property in the ViewModel.
To fix this, I suggest, you set the DisplayHelper as a property in the ViewModel.
I hope it helped, and if you need me to elaborate, feel free to ask. Happy Coding. :)
I am using .NET framework 4.0 to build my application.
I have a combobox in which I want to turn off suggest-append mode of combobox. Instead I want suggest-only mode.
In many questions users ask for turning autoComplete feature off and everywhere I got the same answer. i.e. set IsTextSearchEnabled to False.
When IsTextSearchEnabled = True
When IsTextSearchEnabled = False
What I want is :
When User Presses Enter on the Combobox I want the Item to be appended to the textbox of the combobox.
Is this thing possible in WPF?
Like promised here is the demo. As you can see I did what I explained in my comments. I listened to text changed event.
Check it out:
<Grid>
<local:MyComboBox x:Name="comboBox" IsEditable="True"
VerticalAlignment="Center"
IsTextSearchEnabled="True">
<ComboBoxItem>hello</ComboBoxItem>
<ComboBoxItem>world</ComboBoxItem>
<ComboBoxItem>123</ComboBoxItem>
</local:MyComboBox>
</Grid>
public class MyComboBox : ComboBox
{
private string myValue;
private bool needsUpdate;
public override void OnApplyTemplate()
{
TextBox tbx = this.GetTemplateChild("PART_EditableTextBox") as TextBox;
tbx.PreviewKeyDown += (o, e) =>
{
this.needsUpdate = true;
};
tbx.TextChanged += (o, e) =>
{
if (needsUpdate)
{
myValue = tbx.Text;
this.needsUpdate = false;
}
else
{
tbx.Text = myValue;
}
};
base.OnApplyTemplate();
}
}
I have a requirement for which i need a MultiState Checkboxes in WPF...
Also as i am using MVVM so handling binding and Commands should follow the same as i will use it in my Views.
I have seen a Multistate checkbox in DotnetNuke(made in ASP.NET) but how to make it in WPF
some inputs in this regards will be helpful
Some example will be great...
Checkboxes have specific functionality (checked, unchecked, and optionally indeterminate).
Based on your comment, I would think it would be easiest to just do it as a button. I don't have time now to test out an actual example, but here some pseudocode to get you going:
XAML
<Button Command="{Binding ToggleDecisionState}">
<Button.Content>
<Image Source="{Binding CurrentDecisionIcon}" />
<Button.Content>
</Button>
ViewModel (leaving out MVVM implementation details)
enum Decisions
{
Agree,
Disagree,
Maybe,
DoNotKnow
};
public Decisions CurrentDecision
{
get {}
set {}
}
public RelayCommand ToggleDecisionStateCommand
{
// In here, call code to execute toggle
if (mCurrentDecision == Decisions.DoNotKnow)
CurrentDecision = Decisions.Agree;
else
CurrentDecision += 1;
}
public ImageSource CurrentDecisionIcon
{
get
{
ImageSource img = [some default image];
switch (mCurrentDecision)
{
case Decisions.Agree:
img = [path to Agree icon];
break;
// Other cases here
}
return img;
}
}
if you want to use 3-state check box, you must change the behind boolean value(in ViewModel)
to bool? type.
public bool? IsEnabled { get; set; }
and Set this property for CheckBox
IsThreeState = True;