|
![Go to Parent](https://www.codeproject.com/App_Themes/CodeProject/Img/arrow-up24.png) Thank You!
I have modified my code, but my SelectedContact still doesnt update.
The XAML code for the listbox bindings remains the same as in my first post.
What should i do next?
(sorry if its something obvious that i havent noticed)
public class MainViewModel : ViewModelBase
{
private Contact selectedContact;
private ObservableCollection<Contact> contactList;
private ObservableCollection<Conversation> conversationList;
public MainViewModel()
{
ContactList = new ObservableCollection<Contact>(cbdc.Contacts);
SelectedContact = ContactList.FirstOrDefault();
}
public ObservableCollection<Contact> ContactList
{
get
{
return contactList;
}
private set
{
if (contactList != value)
{
contactList = value;
RaisePropertyChanged();
}
}
}
public ObservableCollection<Conversation> ConversationList
{
get
{
return conversationList;
}
private set
{
if (conversationList != value)
{
conversationList = value;
RaisePropertyChanged();
}
}
}
public Contact SelectedContact
{
get
{
return selectedContact;
}
set
{
if (selectedContact != value)
{
selectedContact = value;
RaisePropertyChanged();
PopulateConversationList();
}
}
}
public void PopulateConversationList()
{
if (selectedContact == null)
{
ConversationList = null;
}
else
{
var conversations = from c in cbdc.Conversations
where c.ContactID == SelectedContact.Id
select c;
ConversationList = new ObservableCollection<Conversation>(conversations);
}
}
}
public class ViewModelBase: INotifyPropertyChanged
{
public ContactLinqToSQLClassesDataContext cbdc = new ContactLinqToSQLClassesDataContext();
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
internal void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
public class ViewModelBase: INotifyPropertyChanged
{
public ContactLinqToSQLClassesDataContext cbdc = new ContactLinqToSQLClassesDataContext();
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
internal void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MainViewModel cvm = new MainViewModel();
ContactsLstBx.ItemsSource = cvm.ContactList;
ConversationLstBx.ItemsSource = cvm.ConversationList;
}
}
|
|
|
|
|
ItemsSource binds to a collection. There is a default CollectionView generated that is what you are actually seeing. The CollectionView listens for a CollectionChanged event not a PropertyChanged event. Now, you may assume that ObservableCollection handles all this for you, and generally it does. But you are not performing any actions that are monitorable by this class (Add, Move, Remove, Replace, Reset[^]) and instead are completely replacing the instance itself. Try this:
public void PopulateConversationList()
{
if (selectedContact == null)
{
ConversationList = null;
}
else
{
var conversations = from c in cbdc.Conversations
where c.ContactID == SelectedContact.Id
select c;
ConversationList.Clear();
foreach (Conversation c in conversations)
{
ConversationList.Add(c);
}
}
}
I highly recommend anyone learning WPF to read the MSDN Data Binding Overview[^] article. If you skip down to "Binding to Collections" you'll see relevant information for both your question and my answer
Haven't had a chance to actually run the code but this definitely seems like the issue since I've run into the same problem before.
|
|
|
|
|
I have modified the PopulateConversationList() method, and before
ConversationList.Clear();
i have added
if (ConversationList == null)
{
ConversationList = new ObservableCollection<Conversation>();
}
because i was getting a NullRefferenceException.
The code works as before(SelectedContact doesnt updates).
What else should i check?
public void PopulateConversationList()
{
if (selectedContact == null)
{
ConversationList = null;
}
else
{
var conversations = from c in cbdc.Conversations
where c.ContactID == SelectedContact.Id
select c;
if (ConversationList == null)
{
ConversationList = new ObservableCollection<Conversation>();
}
ConversationList.Clear();
foreach (Conversation c in conversations)
{
ConversationList.Add(c);
}
}
|
|
|
|
|
The whole point of my post was to remove the re-assignment. Unless you want to implement your own CollectionChanged event to cover the re-assignment, the default CollectionView won't know the collection was modified. I forgot to change the null assignment in your if-statement. Also, you should instantiate in your constructor.
public MainViewModel()
{
ContactList = new ObservableCollection<Contact>(cbdc.Contacts);
ConversationList = new ObservableCollection<Conversation>();
SelectedContact = ContactList.FirstOrDefault();
PopulateConversationList();
}
public void PopulateConversationList()
{
if (selectedContact == null)
{
ConversationList.Clear();
}
else
{
var conversations = from c in cbdc.Conversations
where c.ContactID == SelectedContact.Id
select c;
ConversationList.Clear();
foreach (Conversation c in conversations)
{
ConversationList.Add(c);
}
}
If you really want to remake the collection each time you can but you will have to implement INotifyCollectionChanged to cover your re-assignment which is not as friendly as INotifyPropertyChanged . That's a lot of extra mess when you can simply re-use the collection like this.
|
|
|
|
|
![Go to Parent](https://www.codeproject.com/App_Themes/CodeProject/Img/arrow-up24.png) I have modified the code and i get the same results as before.
Sorry, i dont want to remake the collection, but i am a little(maybe a little more) confused.
Should i check something else?
public class MainViewModel : ViewModelBase
{
private Contact selectedContact;
private ObservableCollection<Contact> contactList;
private ObservableCollection<Conversation> conversationList;
public MainViewModel()
{
ContactList = new ObservableCollection<Contact>(cbdc.Contacts);
ConversationList = new ObservableCollection<Conversation>();
SelectedContact = ContactList.FirstOrDefault();
PopulateConversationList();
}
public ObservableCollection<Contact> ContactList
{
get
{
return contactList;
}
private set
{
if (contactList != value)
{
contactList = value;
RaisePropertyChanged();
}
}
}
public Contact SelectedContact
{
get
{
return selectedContact;
}
set
{
if (selectedContact != value)
{
selectedContact = value;
RaisePropertyChanged();
PopulateConversationList();
}
}
}
public ObservableCollection<Conversation> ConversationList
{
get
{
return conversationList;
}
private set
{
if (conversationList != value)
{
conversationList = value;
RaisePropertyChanged();
}
}
}
public void PopulateConversationList()
{
if (selectedContact == null)
{
ConversationList.Clear();
}
else
{
var conversations = from c in cbdc.Conversations
where c.ContactID == SelectedContact.Id
select c;
ConversationList.Clear();
foreach (Conversation c in conversations)
{
ConversationList.Add(c);
}
}
}
}
|
|
|
|
|
So I mocked up a DB and used your code with some optimizations I explain in comments. Works for me. If you're still having an issue it might be a DB issue or some code that isn't posted. Here's the code I used:
MainWindow.xaml: Nothing important changed, just some formatting for my screen
<Window x:Class="ContactsList.MainWindow"
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"
Title="MainWindow" Height="350" Width="525">
<DockPanel LastChildFill="True">
<ListBox x:Name="ContactsLstBx" Width="200" ItemsSource="{Binding ContactList}" SelectedItem="{Binding SelectedContact, UpdateSourceTrigger=PropertyChanged}" DockPanel.Dock="Left">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox x:Name="ConversationLstBx" Width="200" ItemsSource="{Binding ConversationList}" DockPanel.Dock="Right" HorizontalAlignment="Right" >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Title}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainViewModel ViewModel { get; private set; }
public MainWindow()
{
InitializeComponent();
ViewModel = new MainViewModel();
DataContext = ViewModel;
}
}
MainViewModel.cs
public class MainViewModel : ViewModelBase
{<br />
public ObservableCollection<Contact> ContactList { get; private set; }
public ObservableCollection<Conversation> ConversationList { get; private set; }
private Contact selectedContact;
public Contact SelectedContact
{
get
{
return selectedContact;
}
set
{
if (selectedContact != value)
{
selectedContact = value;
PopulateConversationList();
RaisePropertyChanged();
}
}
}
public MainViewModel()
{
ContactList = new ObservableCollection<Contact>(cbdc.Contacts);
ConversationList = new ObservableCollection<Conversation>();
SelectedContact = ContactList.FirstOrDefault();
}
public void PopulateConversationList()
{
ConversationList.Clear();
if (selectedContact != null)
{
var conversations = cbdc.GetConversations(selectedContact);
foreach (Conversation c in conversations)
ConversationList.Add(c);
}
}
}
ViewModelBase.cs Not really important, just me mocking up DB objects
public class ViewModelBase : INotifyPropertyChanged
{
protected MockDB cbdc;
public event PropertyChangedEventHandler PropertyChanged;
public ViewModelBase()
{
cbdc = new MockDB();
}
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public class MockDB
{
private Dictionary<Contact, List<Conversation>> conversations;
public List<Contact> Contacts { get; private set; }
public MockDB()
{
Contacts = CreateContacts();
conversations = CreateConversations(Contacts);
}
public List<Conversation> GetConversations(Contact c) =>
conversations[c];
private List<Contact> CreateContacts()
{
return new List<Contact>()
{
new Contact("Jim"),
new Contact("Bob"),
new Contact("Mary")
};
}
private Dictionary<Contact, List<Conversation>> CreateConversations(List<Contact> contacts)
{
Dictionary<Contact, List<Conversation>> conversationMap = new Dictionary<Contact, List<Conversation>>();
foreach (Contact c in contacts)
conversationMap.Add(c, CreateConversationList(c));
return conversationMap;
}
private List<Conversation> CreateConversationList(Contact contact)
{
return new List<Conversation>()
{
new Conversation($"Hi {contact.Name}!"),
new Conversation("Pleasant day today.")
};
}
}
public class Contact
{
public string Name { get; private set; }
public Contact(string contactName)
{
Name = contactName;
}
}
public class Conversation
{
public string Title { get; private set; }
public Conversation(string conversationText)
{
Title = conversationText;
}
}
Hopefully this helps ![Smile | :)](https://codeproject.global.ssl.fastly.net/script/Forums/Images/smiley_smile.gif)
|
|
|
|
|
Thank you very much, it works!
I wasnt setting the datacontext right(i was thinking that the datacontext generated by my LinqToSql classes was the only one i have to interact with).
Thank you again!
![Smile | :)](https://codeproject.global.ssl.fastly.net/script/Forums/Images/smiley_smile.gif)
|
|
|
|
|
Richard Deeming wrote: You should also store the event handler delegate in a local variable before testing and calling it, since other threads might modify it between the null test and the invocation.
If C# 6.0 is available in your environment this is no longer necessary with the new null-conditional operator. Just an fyi
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
|
|
|
|
|
|
Thank You!
I will look into this.
|
|
|
|
|
I have a WPF application which consists of a main window containing a TabControl whose items are populated dynamically from the model. This is a simplified version:
<Window x:Class="WpfDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:view="clr-namespace:WpfDemo.View"
xmlns:viewmodel="clr-namespace:WpfDemo.ViewModel"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
Title="MainWindow" Height="350" Width="525" Background="#DCE8F3">
<Grid>
<TabControl Grid.Row="0" x:Name="ViewTabControl" ItemsSource="{Binding ViewModels}" SelectedItem="{Binding SelectedTab, Mode=TwoWay}" IsSynchronizedWithCurrentItem="True" Background="LightBlue" Margin="6">
<TabControl.Resources>
<DataTemplate DataType="{x:Type viewmodel:ImageCaptureViewModel}">
<view:ImageCaptureView />
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodel:ProfileDesignerViewModel}">
<view:ProfileDesignerView/>
</DataTemplate>
</TabControl.Resources>
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
</Grid>
</Window>
The view model class - MainWindowViewModel - has an observable collection called ViewModels:
private ObservableCollection<Object> _viewModels;
public ObservableCollection<Object> ViewModels
{
get
{
if (_viewModels == null)
{
_viewModels = new ObservableCollection<Object>();
}
return _viewModels;
}
}
The views are created thus:
public MainWindowViewModel()
{
_profileDesignerViewModel = new ViewModel.ProfileDesignerViewModel();
ViewModels.Add(_profileDesignerViewModel);
_imageCaptureViewModel = new ViewModel.ImageCaptureViewModel();
ViewModels.Add(_imageCaptureViewModel);
}
So far so good. Unfortunately this has a peculiarity. A view is instantiated each time the user selects the corresponding tab. The problem is that the old view is not disposed until the application shuts down.
Does anyone know how to ensure that the old view is disposed, or how to keep the old view and not create a new one? This is creating a serious memory problem for us.
|
|
|
|
|
The WPF TabControl is well known for not persisting views. Fortunately, there are many implementations that work around this - Sacha Barber has one such here[^]
This space for rent
|
|
|
|
|
Thanks. I tried that code (from a different web site) some months back for another project and it did not work. However, after reading your post I found the one below which works:
c# - Stop TabControl from recreating its children - Stack Overflow[^]
It is the exact same control class as per your link, but with the addition of a style in the XAML. I cannot as yet see why the syle makes the difference to the behaviour.
Thanks again, very helpful.
|
|
|
|
|
I am not familiar with silverlight application, can anyone suggest if there is a way to automate the silverlight application using testng - selenium - java framework.
|
|
|
|
|
I've not used selenium but there seems to be quite a few who have! Google[^]
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
I found some supports like silvernium which are used for selenium, but not for eclipse java. still finding the solution
|
|
|
|
|
Good Day all
i am working on a Kinect Sensor Program, but i think this question can best be answered by the WPF forum.
In my Kinect Project , the User can use Kinect Gestures or Voice Commands. THe Gestures work nicely its like one is using a mouse. i have a challenge with the Voice Commands which follow a certain Process. Please note that this functionality works well when one is not using voice commands.
Let me make a simple example.
i have a 3 Buttons
Button 1 = "System Ok "
Button 2 = " View Error"
Button 3 = "System Reboot"
Step 1: Now the logic say , if a user enter a wrong command , the System must play a sound for Error and the button for " View Error" must Animate.
Step 2: The Rule goes on and say if the User has not click the " View Error" within 5 seconds , Stop the Animation on the " View Error"
Step 3: and Animate the "System Reboot" and play the Sound for "System Reboot"
Now on a normal button click with a Mouse , this would work. But now i am not clicking the Button Physically, i am using Voice Commands and i have trapped the SpeechRecognized event which is defined like this
private void SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
{
const double ConfidenceThreshold = 0.3;
const int DegreesInRightAngle = 90;
const int DisplacementAmount = 60;
this.ClearRecognitionHighlights();
if (e.Result.Confidence >= ConfidenceThreshold)
{
switch (e.Result.Semantics.Value.ToString())
{
case "VIEWERROR":
Error_Escaped = true;
btnViewError_Click(null, null);
break;
case "SYSTEMREBOOT":
btnSystemReboot_Click(null, null);
break;
case "SYSTEMSTATUS":
SystemStatus_Click(null, null);
break;
}
}
else
{
this.Status.Content = Properties.Resources.GrammarConfidenceLow;
Logger.NormalLog("The Confidence is low , the Grammar is not Recognised as a Command", Generics.Generics.ActivePage) ;
}
}
as you can see i am clicking the Buttons programmatically. So if i don't click the "View Error" button within 5 seconds , the System Reboot will start playing another sound.
That was about the Process. Now my challenge is that. After i used a wrong option according to my Process, the "View Error" button will give a sound and i will give a Voice Command to Stop the Sound , the next in the Process will still be fired. So i debugged the code and i realised that the Program , runs all the code that was running and when its done, it hit the "SpeechRecognized" event later, i want to intercept and give the Voice Command the same ability as a button click done with a mouse can do.
This is a function that runs , when there is an Error
private void ViewError()
{
Error_Escaped = false;
ResetBlinking(this.btnViewError);
AnimateButton(this.btnViewError);
Generics.Generics.WaitNSeconds(5);
if (Error_Escaped == false)
{
SystemReboot();
}
}
This will set the Flag for "Error_Escape = false " Which means no one has clicked the button. and it will Animate the Button and wait for 5 second which is my custom interval calculator. Now while its waiting i would keep saying some Voice Command to Stop it. it will still continue while i wait , it will jump to System Reboot which will have its sound. i want the
SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
event to fire when i need it not after the other event of buttons are fired.
Tanks
Vuyiswa Maseko
Vuyiswa Maseko,
Spoted in Daniweb-- Sorry to rant. I hate websites. They are just wierd. They don't behave like normal code.
C#/VB.NET/ASP.NET/SQL7/2000/2005/2008
http://www.vimalsoft.com
vuyiswa[at]vimalsoft.com
|
|
|
|
|
I would launch a timer from ViewError(); instead of putting it into a wait state.
The speech recognition event can kill the timer if it still running; else the timer sets off whatever needs to be done after time has elapsed.
(A basic "timeout" pattern).
|
|
|
|
|
I'm working on a WPF application for my company that can show the routing of cables on picture.
The Goal is to let the user choose a Part Number of a cable and the cable routing will be shown on the picture of the tool.
I will make two kinds of Access Control:
- Guest - that give only Read-Only access
- Admin - that will give the possibility to change the route on the picture (for example: adding new points, adding labels and connections between points) with Admin Panel
My problem is that i can't move the Label with the mouse, it's working with the point (the point is Path) and the Line.
Also i would like to understand why when i'm clicking on the Label it recognizes it like a TextBlock.
Here's my code:
public void Label_Edit_Mode(MouseButtonEventArgs e, Canvas canvas, bool lbledit)
{
if (lbledit == true)
{
IInputElement clickedElement = Mouse.DirectlyOver;
if (clickedElement is TextBlock)
{
label = clickedElement as TextBlock;
Mouse.Capture(label);
captured = true;
Vector offset = VisualTreeHelper.GetOffset(label);
x_label = offset.X;
x_canvas = e.GetPosition(canvas).X;
y_label = offset.Y;
y_canvas = e.GetPosition(canvas).Y;
}
}
}
public void Move_Points(MouseEventArgs e, Canvas canvas, bool isclicked)
{
if (e.LeftButton != MouseButtonState.Released)
{
if (captured == true && isclicked == false)
{
double x = e.GetPosition(canvas).X;
double y = e.GetPosition(canvas).Y;
{
if (path != null)
{
x_path += x - x_canvas;
Canvas.SetLeft(path, x_path);
x_canvas = x;
y_path += y - y_canvas;
Canvas.SetTop(path, y_path);
y_canvas = y;
}
else if (label != null)
{
x_label += x - x_canvas;
Canvas.SetLeft(label, x_label);
x_canvas = x;
y_label += y - y_canvas;
Canvas.SetTop(label, y_label);
y_canvas = y;
}
else if (line != null)
{
x_line += x - x_canvas;
Canvas.SetLeft(line, x_line);
x_canvas = x;
y_line += y - y_canvas;
Canvas.SetTop(line, y_line);
y_canvas = y;
}
}
}
}
else
{
captured = false;
}
}
here's a picture of how my application looks like:
[My App]
Thank you for your help
|
|
|
|
|
Dadou55 wrote: Also i would like to understand why when i'm clicking on the Label it recognizes it like a TextBlock.
Look at the remarks on the Mouse.DirectlyOver property:
Controls can be composed of multiple elements. DirectlyOver reports the specific element in the composite control the mouse pointer is over and not the control itself. For example, depending on which part of a Button the pointer is over, the DirectlyOver property could report the TextBox of the Content property or the ButtonChrome .
A Label[^] is a ContentControl . When you add text to it, that gets added as a TextBlock . When the mouse is over the text, DirectlyOver returns the TextBlock from the Content part of the Label control.
Basically, you're looking at the visual tree, not the logical tree. You'll need to use VisualTreeHelper.GetParent[^] to walk up the visual tree until you find the control you're looking for.
Trees in WPF[^]
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
I want to group , ungroup canvas user control in wpf application.Though this can be achieved with microsoft visual studio in built function Group and Ungroup which we get from Object and Timeline window.Through ungrouping we are removing unwanted canvas and making only one parent(canvas) to child controls.
Phis i need to achieve programmatically.Please suggest how can I achieve this through programmatically though I want to process my xaml file without doing this manual step and handle it through code behind.
Below mentioned is code snippet of xaml data which is not yet grouped and after that grouping.
Code before ungrouping :
<Canvas Name="_01" RenderTransform="1, 0, 0, 1, 130.919685, -4.32">
<Canvas Name="_64" RenderTransform="-0.999991, 0.004316, -0.004316, -0.999991, 14.637921, 2145.991988">
<Path Name="_64_G0__MrkrEnd" Visibility="Collapsed" RenderTransform="3.31258, -1.721798, -1.721798, -3.31258, 5.998991, 1066.51392" StrokeLineJoin="Round" Data="M -1 -1 L 0 0 L 1 -1 ">
</Path>
</Canvas>
</Canvas>
Code after Ungrouping and grouping into a canvas, which allow a single parent node to the child.
<Canvas Name="_01" RenderTransform="1, 0, 0, 1, 130.919685, -4.32">
<Path x:Name="_64_G0__MrkrEnd" Visibility="Collapsed" StrokeLineJoin="Round" Data="M -1 -1 L 0 0 L 1 -1 " Canvas.Left="14.637" RenderTransformOrigin="0.5,0.5" Canvas.Top="2145.992"/>
</Canvas>
|
|
|
|
|
Since WPF is one of your "specialties", you would know that most "container" controls have a "Children" collection; and to "ungroup" and (re)group controls, you remove them from one collection, and add them back to another collection (of a different "parent" control).
|
|
|
|
|
We are building a WPF project to control a machine. The machine is controlled by a PLC, and link to the PLC is with OPC.
The update from the PLC is done via reflexion, so the 'set' is called, but the disadvantage of this is that the value is written to the PLC again via 'SetValueToPlc()' see example.
The easy solution would be to copy all properties with a Write to PLC variant, but since our project already contains more than 2000 properties this is not the prefered solution.
The way I'm thinking is to make use of a template like the Nullable, and create 2 separate 'setters'. One which is coupled to the ViewModel for updates from the View and business-logic. And the other can be use by the OPC updates (reflection). The implementation from the last would be without writing the value back to the PLC.
So my question is how to make such a template, or are there other sugestions?
Example property:
[OpcItem("Simulate")]
public Boolean Simulate
{
get { return _simulate; }
set
{
_simulate = value;
SetValueToPlc(MethodBase.GetCurrentMethod().MethodHandle, value);
RaisePropertyChanged();
}
}
|
|
|
|
|
If you're worried about "redundant updates to the PLC", you should start by checking for a "value change" in the setter instead of blindly using the value.
|
|
|
|
|
The problem is when a value is only changed shortly in the plc. Eg a reset, goes to True, and the next cyclus is reset to false. The value true is set to the OPC point (old value was false, so this is updated to true, en send to the PLC again. But the plc already handled this True, so it will get an other reset (was already false and is reset to true). So one reset will lead to a second reset.
The update rate of OPC is slower than the PLC so this will stop after the second.
|
|
|
|
|