Using d-bus to change volume with bluez 5.43 on C.H.I.P - dbus

I'm new to d-bus and bluez. I have a CHIP module, running as a BT speaker. I followed this:
https://github.com/hadess/CHIP-bluetooth-speaker
and also the instructions in the following link to get iPhone volume working:
https://github.com/hadess/CHIP-bluetooth-speaker/issues/8
I'd like to be able to change the volume of the speaker both from my iphone and from the module - so I'm guessing dbus commands is the way to go.
I've successfully connected and disconnected from my iPhone using:
dbus-send --system --print-reply --dest=org.bluez /org/bluez/hci0/dev_A0_D7_95_A9_88_91 org.bluez.Device1.Disconnect
and d-feet was useful to show me how to do it. However, I'm not clear on how I can do play/pause/volumeUp/volumeDown. The documentation here:
https://kernel.googlesource.com/pub/scm/bluetooth/bluez/+/5.43/doc/media-api.txt
explains how to do play/pause but I can't get it to work. I've tried:
dbus-send --system --print-reply --dest=org.bluez /org/bluez/hci0/dev_A0_D7_95_A9_88_91 org.bluez.MediaPlayer1.Pause
and:
dbus-send --system --print-reply --dest=org.bluez /org/bluez/hci0/dev_A0_D7_95_A9_88_91/Player1 org.bluez.MediaPlayer1.Pause
with the same error:
Error org.freedesktop.DBus.Error.UnknownMethod: Method "Pause" with signature "" on interface "org.bluez.MediaPlayer1" doesn't exist
and I'm not at all clear how to change volume (the VolumeUp and VolumeDown commands in mediacontrol1 as shown as deprecated).
Can anyone help?
Update
After great comments from Constantin below (thanks), I'd like some more clarification.
Using d-feet, I get the following information for my device:
should I be able to see a "MediaTransport1" and/or "MediaControl1" interface entry in the listing?
When I call the introspective for device1, I get:
'<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"\n"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">\n<node><interface name="org.freedesktop.DBus.Introspectable"><method name="Introspect"><arg name="xml" type="s" direction="out"/>\n</method></interface><interface name="org.bluez.Device1"><method name="Disconnect"></method><method name="Connect"></method><method name="ConnectProfile"><arg name="UUID" type="s" direction="in"/>\n</method><method name="DisconnectProfile"><arg name="UUID" type="s" direction="in"/>\n</method><method name="Pair"></method><method name="CancelPairing"></method><property name="Address" type="s" access="read"></property><property name="Name" type="s" access="read"></property><property name="Alias" type="s" access="readwrite"></property><property name="Class" type="u" access="read"></property><property name="Appearance" type="q" access="read"></property><property name="Icon" type="s" access="read"></property><property name="Paired" type="b" access="read"></property><property name="Trusted" type="b" access="readwrite"></property><property name="Blocked" type="b" access="readwrite"></property><property name="LegacyPairing" type="b" access="read"></property><property name="RSSI" type="n" access="read"></property><property name="Connected" type="b" access="read"></property><property name="UUIDs" type="as" access="read"></property><property name="Modalias" type="s" access="read"></property><property name="Adapter" type="o" access="read"></property></interface><interface name="org.freedesktop.DBus.Properties"><method name="Get"><arg name="interface" type="s" direction="in"/>\n<arg name="name" type="s" direction="in"/>\n<arg name="value" type="v" direction="out"/>\n</method><method name="Set"><arg name="interface" type="s" direction="in"/>\n<arg name="name" type="s" direction="in"/>\n<arg name="value" type="v" direction="in"/>\n</method><method name="GetAll"><arg name="interface" type="s" direction="in"/>\n<arg name="properties" type="a{sv}" direction="out"/>\n</method><signal name="PropertiesChanged"><arg name="interface" type="s"/>\n<arg name="changed_properties" type="a{sv}"/>\n<arg name="invalidated_properties" type="as"/>\n</signal>\n</interface></node>'
Which doesn't have any details in for volume, transport control etc. Does that mean my bluez setup isn't permitting it?
Ultimately, I'd like to get this all working within Python so, Constantin, if you can continue your great support, some examples of python code would also be great.
Update 2
Ok, so trying this on a Rpi V3 with Bluez v5.23 and I get:
pi#raspberrypi:~ $ qdbus --system org.bluez /org/bluez/hci0/dev_A0_D7_95_A9_88_91
method QString org.freedesktop.DBus.Introspectable.Introspect()
property read QDBusObjectPath org.bluez.Device1.Adapter
property read QString org.bluez.Device1.Address
property readwrite QString org.bluez.Device1.Alias
property read ushort org.bluez.Device1.Appearance
property readwrite bool org.bluez.Device1.Blocked
property read uint org.bluez.Device1.Class
property read bool org.bluez.Device1.Connected
property read QString org.bluez.Device1.Icon
property read bool org.bluez.Device1.LegacyPairing
property read QString org.bluez.Device1.Modalias
property read QString org.bluez.Device1.Name
property read bool org.bluez.Device1.Paired
property read short org.bluez.Device1.RSSI
property readwrite bool org.bluez.Device1.Trusted
property read QStringList org.bluez.Device1.UUIDs
method void org.bluez.Device1.CancelPairing()
method void org.bluez.Device1.Connect()
method void org.bluez.Device1.ConnectProfile(QString UUID)
method void org.bluez.Device1.Disconnect()
method void org.bluez.Device1.DisconnectProfile(QString UUID)
method void org.bluez.Device1.Pair()
method QDBusVariant org.freedesktop.DBus.Properties.Get(QString interface, QString name)
method QVariantMap org.freedesktop.DBus.Properties.GetAll(QString interface)
signal void org.freedesktop.DBus.Properties.PropertiesChanged(QString interface, QVariantMap changed_properties
method void org.freedesktop.DBus.Properties.Set(QString interface, QString name, QDBusVariant value)
property read bool org.bluez.MediaControl1.Connected
method void org.bluez.MediaControl1.FastForward()
method void org.bluez.MediaControl1.Next()
method void org.bluez.MediaControl1.Pause()
method void org.bluez.MediaControl1.Play()
method void org.bluez.MediaControl1.Previous()
method void org.bluez.MediaControl1.Rewind()
method void org.bluez.MediaControl1.Stop()
method void org.bluez.MediaControl1.VolumeDown()
method void org.bluez.MediaControl1.VolumeUp()
... so MediaControl1 is shown.
Question is, is it due to the different version of Bluez or the different platform? It's also a bit slow to respond to the dbus commands (which I wasn't expecting). Sometimes it's almost immediate, other times is could take 1-2 seconds. Is that expected?

First of all, you were in the right way when adding "Player1" to your object path, the documentation clearly states :
Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/playerX
However, how did you find that "Player1" ? This must not be a random number, you will find the exact string you have to append to the mac address by Introspecting your device (object path with dev_XX_XX...), usually in an xml tag ''
Now about your error, I know the documentation states that the Pause method does not take arguments, though maybe the documentation is not up-to-date ?
One easy way to know a bit more about methods from the dbus is to analyze introspection :
You can use the following command to retrieve the introspection (you should update the path after hci0 to match your object path) :
dbus-send --system --print-reply --dest=org.bluez /org/bluez/hci0 org.freedesktop.DBus.Introspectable.Introspect
To give you an example, the following is extracted from an introspection :
<method name="GetAll"><arg name="interface" type="s" direction="in"/><arg name="properties" type="a{sv}" direction="out"/>
From this I learn that the GetAll method takes as argument (direction in, type s) the interface name as a string, and returns a dictionary where strings(keys) are mapped to variants(values) (direction out, type a{sv})
Could you introspect your device and make sure you find that "Player1" node tag ? Then introspect that Player1 and make sure the Pause method does not take any argument.
To change the volume, I guess you're supposed to the use "Volume" property from the Interface org.bluez.MediaTransport1 (look at the end of the media-api.txt file).
Finally, do you plan to include these manipulations in a program ? If yes, I suggest using dbus bindings in the appropriate language (C, Glib, Python, C#...). I can give you more documentation on how to interact with bluez in a program.
UPDATE
The version on your rpi (5.23) is old and uses an API which is now deprecated (see for yourself the org.bluez.MediaControl1 interface has [Deprecated] everywhere here https://git.kernel.org/cgit/bluetooth/bluez.git/tree/doc/media-api.txt)
By looking at the doc, theres no interfaces you should be able to see when introspecting the device. org.bluez.Media1 should appear if you introspect /org/bluez/hci0 (refering to the specified Object path).
Service org.bluez
Interface org.bluez.Media1
Object path [variable prefix]/{hci0,hci1,...}
And org.bluez.MediaPlayer1 should appear when introspecting the player :
Service org.bluez (Controller role)
Interface org.bluez.MediaPlayer1
Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/playerX
Though in your case, I see nothing related to that "playerX" in your introspection and that's the issue that need to be fixed first.
To stream sound over Bluetooth, you want to use the A2DP Protocol with your linux as "source" and your speaker as "sink". This functionality (audio streaming) might require PulseAudio. Try to add the latest version of pulseaudio to your system (you might want to compile from sources). See here for the details : https://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/Bluetooth/
Maybe your rpi uses bluez4 and pulseaudio configured to connect automatically. This would explain why you can see the interfaces on the rpi.
There is also the Arch Wiki that can help for troubleshooting: https://wiki.archlinux.org/index.php/Bluetooth_headset#Headset_via_Bluez5.2FPulseAudio
While I was looking for information I found this website : http://www.lightofdawn.org/wiki/wiki.cgi/BluezA2DP
It describes how to use the bluez api to make it work for A2DP. The tutorial was written for bluez4 and the author talks about bluez5 compatibility at the end.
I can't tell exactly what actions and in what order you'll need to do, although you will find plenty of documentation and tutorials when googling "bluez a2dp python" (like this one on reddit, read carefully his comments at the top of his code file) : https://www.reddit.com/r/Python/comments/1f1xkt/use_python_to_turn_your_bluetooth_laptop_into_a/)
My guess is that you'll need to scan (org.bluez.Adapter1) ,register endpoint (org.bluez.Media1), pair (and maybe connect ? using org.bluez.Device1)and acquire a file descriptor for writing (org.bluez.MediaTransport1)
Before doing anything else, you'll need to see thoses interfaces using d-feet ! Once you have it working with d-feet, you can refer to this gist for a simple python example that will perform a scan : https://gist.github.com/CynaCons/8eb02540f87af5594fac489a9dca32c1
I've also uploaded an advanced version on github gist.

Related

dbus-send fails due to QDBusRawType

The introspection of the service returns the below for the method I'm interested in
method bool org.my.connectionInterface.sendParam(QDBusRawType::(is param))
However, when I try to send a message, I get this error
dbus-send --system --print-reply --dest=org.my.connectionInterface /Connection1 org.my.connectionInterface.sendParam int32:1 string:"jolly-string"
Error org.freedesktop.DBus.Error.UnknownMethod: No such method 'sendParam' in interface 'org.my.connectionInterface' at object path '/Connection1' (signature 'is')
I can call the methods with no parameters or simple parameters types, so, I presume the issue is with QDBusRawType. Is there a way to wrap the parameters in QDBusRawType?
I would not expect int32: and string: to be required as the method has the signature of is so it knows that the first parameter is an int32 and the second a string.
dbus-send --system --print-reply --dest=org.my.connectionInterface /Connection1 org.my.connectionInterface.sendParam 1 "jolly-string"
If that doesn't work, can you update your question with the output using:
busctl introspect SERVICE OBJECT [INTERFACE]

Configuration file

I need to read some property from configuration file. I don't want to store the property file inside the location. What is best practice
For example, if execute as follows
java -jar payara-micro.jar --deploy demo.jar
I want to keep the parameter file where payara-micro.jar located. I need to read the property file inside the war file. How to achieve it.
Thank you.
You may first start payara-micro with --rootDir path option. Payara treats this dir as working, so it creates `config' dir there. Then just edit domain.xml file as you need and start payara-micro again. All resources you create will be available at you beans as usual. For example you may add some properties like this:
...
<resources>
<jdbc-resource pool-name="DerbyPool" jndi-name="jdbc/__default" object-type="system-all" />
<jdbc-connection-pool is-isolation-level-guaranteed="false" name="DerbyPool" datasource-classname="org.apache.derby.jdbc.EmbeddedDataSource" res-type="javax.sql.DataSource">
<property name="databaseName" value="${com.sun.aas.instanceRoot}/lib/databases/embedded_default" />
<property name="connectionAttributes" value=";create=true" />
</jdbc-connection-pool>
<connector-connection-pool max-pool-size="250" steady-pool-size="1" name="jms/__defaultConnectionFactory-Connection-Pool" resource-adapter-name="jmsra" connection-definition-name="javax.jms.ConnectionFactory"></connector-connection-pool>
<connector-resource pool-name="jms/__defaultConnectionFactory-Connection-Pool" jndi-name="jms/__defaultConnectionFactory" object-type="system-all-req"></connector-resource>
<context-service description="context service" jndi-name="concurrent/__defaultContextService" object-type="system-all"></context-service>
<managed-executor-service maximum-pool-size="200" core-pool-size="1" long-running-tasks="true" keep-alive-seconds="300" hung-after-seconds="300" task-queue-capacity="20000" jndi-name="concurrent/__defaultManagedExecutorService" object-type="system-all"></managed-executor-service>
<managed-scheduled-executor-service core-pool-size="1" long-running-tasks="true" keep-alive-seconds="300" hung-after-seconds="300" jndi-name="concurrent/__defaultManagedScheduledExecutorService" object-type="system-all"></managed-scheduled-executor-service>
<managed-thread-factory description="thread factory" jndi-name="concurrent/__defaultManagedThreadFactory" object-type="system-all"></managed-thread-factory>
<custom-resource factory-class="org.glassfish.resources.custom.factory.PropertiesFactory" res-type="java.util.Properties" jndi-name="myconf">
<property name="some.my.property" value="some.value"></property>
</custom-resource>
</resources>
(see custom-resource tag)
Then just inject it into you bean:
#Resource(type=java.util.Properties.class, name="myconf")
private final Properties parameters;
Also you may specify --domainConfig file to keep configuration anywhere you want.
Use --help to see full options list.
You can pass system properties to the payara micro using a command line argument, like this:
java -jar payara-micro.jar --deploy app.war --systemProperties=sys.properties
Also check out the Payara micro documentation about this option.
You can pass system properties configured in domain.xml file. This overrides the default domain.xml.
java -jar payara-micro.jar --domainConfig domain.xml --deploy app.war
You can get the default domain.xml from the payara-micro.jar

Solr 4: disable compression on stored fields: how to actually configure custom codec?

The short question is :
I want to disable stored field compression on Solr 4.3.0 index. After reading :
http://blog.jpountz.net/post/35667727458/stored-fields-compression-in-lucene-4-1
http://wiki.apache.org/solr/SimpleTextCodecExample
http://www.opensourceconnections.com/2013/06/05/build-your-own-lucene-codec/
I've decided to follow the path described there, and make my own codec. I'm pretty sure I've followed all the steps, however, when I actually try to use my codec (affectionatelly named "UncompressedStorageCodec"), I get the following error in Solr log:
java.lang.IllegalArgumentException: A SPI class of type org.apache.lucene.codecs.PostingsFormat with name 'UncompressedStorageCodec' does not exist. You need to add the corresponding JAR file supporting this SPI to your classpath.
The current classpath supports the following names: [Pulsing41, SimpleText, Memory, BloomFilter, Direct, Lucene40, Lucene41]
at org.apache.lucene.util.NamedSPILoader.lookup(NamedSPILoader.java:109)
From the output I get that Solr is not picking up the jar with my custom codec, and I don't get why?
Here's all the horriffic details:
I've created a class like this:
public class UncompressedStorageCodec extends FilterCodec {
private final StoredFieldsFormat fieldsFormat = new Lucene40StoredFieldsFormat();
protected UncompressedStorageCodec() {
super("UncompressedStorageCodec", new Lucene42Codec());
}
#Override
public StoredFieldsFormat storedFieldsFormat() {
return fieldsFormat;
}
}
in package: "fr.company.project.solr.transformers.utils"
The FQDN of "FilterCodec" is: "org.apache.lucene.codecs.FilterCodec"
I've created a basic jar file out of this (exported it as jar from Eclipse).
The Solr installation I'm using to test this is the basic Solr 4.3.0 unzipped, and started via it's embedded Jetty server and using the example core.
I've placed my jar with the codec in [solrDir]\dist
In:
[solrDir]\example\solr\myCore\conf\solrconfig.xml
I've added the line:
<lib dir="../../../dist/" regex="myJarWithCodec-1.10.1.jar" />
Then in the schema.xml file, I've declared some fieldTypes that should use this codec like so:
<fieldType name="string" class="solr.StrField" sortMissingLast="true" omitNorms="true" postingsFormat="UncompressedStorageCodec"/>
<fieldType name="string_lowercase" class="solr.TextField" positionIncrementGap="100" omitNorms="true" postingsFormat="UncompressedStorageCodec">
<!--...-->
</fieldType>
Now, if I use the DataImportHandler component to import some data into Solr, at commit time it tells me:
java.lang.IllegalArgumentException: A SPI class of type org.apache.lucene.codecs.PostingsFormat with name 'UncompressedStorageCodec' does not exist. You need to add the corresponding JAR file supporting this SPI to your classpath.
The current classpath supports the following names: [Pulsing41, SimpleText, Memory, BloomFilter, Direct, Lucene40, Lucene41]
at org.apache.lucene.util.NamedSPILoader.lookup(NamedSPILoader.java:109)
What I find strange is that the above mentioned codec jar also contains some Transformers for the DataImportHandler component. And those are picked up fine. Also, other jars placed in the dist folder (and declared in the same way in solrconfig.xml), like the jdbc driver are picked up fine. I'm guessing that for the codec there's this SPI thingy which loads things differentlly, and there's somethign he's missing...
I've also tried placing the codec jar in:
[solrDir]\example\solr-webapp\webapp\WEB-INF\lib\
as well as inside the WEB-INF\lib folder of the solr.war file, which is found in:
[solrDir]\example\webapps\
but I'm still getting the same error.
So basically, my question is, what's missing so that my codec jar is picked up by Solr?
Thanks
I'm going to answer this question myself since it sort of become moot due to some benchmarks I've made: long story short, I had arrived at the (wrong) conclusion that for really large stored fields, Solr 3.x and 4.0 (without field compression) is faster than Solr 4.1 and above (with field compression). However that was mostly due to some errors in my benchmarks. After repeating them I've obtained results where when you go from non-compressed to compressed fields even for very large stored fields, the index time is between 0% and 15% slower, which is really not bad at all, considering that afterwards queries on the compressed fields indexes are 10-20% times faster (the document fetching part).
Also, here's some remarks on how to speed up indexing:
Use the DataImportHandler plugin. It bypasses the Solr Rest (HTTP based) API and writes directly to the Lucene index.
Check out said plugins sources to see how it accomplishes this, and do your own plugin if the DataImportHandler doesn't meet your needs
If for whatever reason you want to stick to the Solr Rest API, use ConcurrentUpdateSolrServer and play around with the queue size and number of threads parameters. It will normally be a lot faster (up to 200% in my case) than the basic HttpSolrServer.
Don't forget to enable the javabin data serialization like this:
ConcurrentUpdateSolrServer solrServer = new ConcurrentUpdateSolrServer("http://some.solr.host:8983/solr", 100, 4);
solrServer.setRequestWriter(new BinaryRequestWriter());
I'm explicitly showing the code because I believe there migth be a small bug here:
If you look at the ConcurrentUpdateSolrServer constructor, you'll see that by default it already sets the request writer to binary:
//the ConcurrentUpdateSolrServer initializes HttpSolrServer objects using this constructor:
public HttpSolrServer(String baseURL, HttpClient client) {
this(baseURL, client, new BinaryResponseParser());
}
However after debugging I've noticed that if you don't explicitly call the setWriter method with the Binary writer argument, it will still use the XmlSerializer.
Going from XML to Binary serialization reduces the size of my documents about 3 times as they are being sent to the server. This makes my index times for this case about 150-200% faster.
I have recently tried and succeeded to get something very similar to work. The only difference is that I want to enable the best compression instead of no compression, and Solr defaults to the fastest compression. I also got the "SPI class [...] does not exist" error at some point, and here is what I have found out from various articles, including the ones you have linked to.
Lucene uses SPI to find the codec classes to load. Lucene requires the list of codec classes be declared in the file "org.apache.lucene.codecs.Codec", and the file must be on the class path. To get Solr to load the file: When you create your JAR file "myJarWithCodec-1.10.1.jar", make sure that it contains a file at "META-INF/services/org.apache.lucene.codecs.Codec". The file should have one full class name per line, like this:
org.apache.lucene.codecs.lucene3x.Lucene3xCodec
org.apache.lucene.codecs.lucene40.Lucene40Codec
org.apache.lucene.codecs.lucene41.Lucene41Codec
org.apache.lucene.codecs.lucene42.Lucene42Codec
fr.company.project.solr.transformers.utils.UncompressedStorageCodec
And in solrconfig.xml, replace:
<codecFactory class="solr.SchemaCodecFactory" />
with:
<codecFactory class="fr.company.project.solr.transformers.utils.UncompressedStorageCodec" />
You might also need to remove postingsFormat="UncompressedStorageCodec" from schema.xml if Solr complains. I think this particular parameter is for specifying the postings format, not the codec. Hope it helps.

mybatis config problem

I'm new to MyBatis.
Ive been trying to configure mybatis in a webservice I'm writing but with no luck yet.
What I've done already is,
UserInfoMapper interface
UserInfoMapper.xml with mapper namespace with my UserInfoMapper interface and a select
mybatis-config.xml with typeAlias to use as result type in UserInfoMapper.xml
dataSource bean for oracle (I get connected) in datasourceContext.xml
org.mybatis.spring.mapper.MapperScannerConfigurer bean with basePackage pointing to my UserInfoMapper interface in datasourceContext.xml
sqlSessionFactory bean ie. org.mybatis.spring.SqlSessionFactoryBean with property for my dataSource and configLocation
userInfoMapper bean ie. org.mybatis.spring.mapper.MapperFactoryBean with property mapperInterface (value="is.simnn.act.web.ngs.persistence.UserInfoMapper") and sqlSessionFactory property (ref="sqlSessionFactory") in datasourceContext.xml
then in my applicationContext.xml I have following,
<import resource="classpath:META-INF/wsContext.xml" />
<import resource="classpath:META-INF/db/datasourceContext.xml" />
In my test case I keep getting NullPointerException when I call jaxws:endpoint and it leads me to my UserInfoMapper interface.
Any idea or hints to what might be wrong with my config?
Thanks,
Gunnlaugur
It is hard to comment without having more information. Can you post your UserInfoMapper.java interface, your UserInfoMapper.xml and your stack trace, please? Are you certain that the method name in your interface matches the ID of your SELECT in the XML?

How to detect broken WPF Data binding?

While trying to answer a question in the vicinity 'Unit Testing WPF Bindings' I had the following niggling question..
What's the best way to find if you have WPF Data Binding wiring setup incorrectly (or you just broke something that was wired up correctly) ?
Although the unit-testing approach seems to be like Joel's 'ripping off your arm to remove a splinter'.. I am looking around for easier less Overhead ways to detect this.
Everyone seems to have committed themselves to data binding in a big way with WPF.. and it does have its merits.
In .NET 3.5 it was introduced a new way to specifically output tracing information about specific data bindings.
This is done through the new System.Diagnostics.PresentationTraceSources.TraceLevel attached property that you can apply to any binding or data provider. Here is an example:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
Title="Debug Binding Sample"
Height="300"
Width="300">
<StackPanel>
<TextBox Name="txtInput" />
<Label>
<Label.Content>
<Binding ElementName="txtInput"
Path="Text"
diag:PresentationTraceSources.TraceLevel="High" />
</Label.Content>
</Label>
</StackPanel>
</Window>
This will put trace information for just that particular binding in Visual Studio's Output Window, without any tracing configuration required.
Best I could find...
How can I debug WPF Bindings? by Beatriz Stollnitz
Since everyone can't always keep one eye on the Output Window looking for Binding errors, I loved Option#2. Which is add this to your App.Config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.diagnostics>
<sources>
<source name="System.Windows.Data" switchName="SourceSwitch" >
<listeners>
<add name="textListener" />
</listeners>
</source>
</sources>
<switches>
<add name="SourceSwitch" value="All" />
</switches>
<sharedListeners>
<add name="textListener"
type="System.Diagnostics.TextWriterTraceListener"
initializeData="GraveOfBindErrors.txt" />
</sharedListeners>
<trace autoflush="true" indentsize="4"></trace>
</system.diagnostics>
</configuration>
Pair that up with a good regex scan script to extract out relevant info, that you can run occasionally on the GraveOfBindErrors.txt in your output folder
System.Windows.Data Error: 35 : BindingExpression path error: 'MyProperty' property not found on 'object' ''MyWindow' (Name='')'. BindingExpression:Path=MyProperty; DataItem='MyWindow' (Name=''); target element is 'TextBox' (Name='txtValue2'); target property is 'Text' (type 'String')
I use the solution presented here to turn binding errors into native Exceptions: http://www.jasonbock.net/jb/Default.aspx?blog=entry.0f221e047de740ee90722b248933a28d
However, a normal scenario in WPF bindings is to throw exceptions in case the user input cannot be converted to the target type (for instance, a TextBox bound to a integer field; the input of a non-numeric string results in a FormatException, the input of number that is too large results in an OverflowException). A similar case is when the Setter of the source property throws an exception.
The WPF way of handling this is via ValidatesOnExceptions=true and ValidationExceptionRule to signal the user the supplied input is not correct (using the exception message).
However, these exception are also send to the output window and thus 'caught' by the BindingListener, resulting in an error...clearly not the behaviour you'd want.
Therefore, I expanded the BindingListener class to NOT throw an Exception in these cases:
private static readonly IList<string> m_MessagesToIgnore =
new List<String>()
{
//Windows.Data.Error 7
//Binding transfer from target to source failed because of an exception
//Normal WPF Scenario, requires ValidatesOnExceptions / ExceptionValidationRule
//To cope with these kind of errors
"ConvertBack cannot convert value",
//Windows.Data.Error 8
//Binding transfer from target to source failed because of an exception
//Normal WPF Scenario, requires ValidatesOnExceptions / ExceptionValidationRule
//To cope with these kind of errors
"Cannot save value from target back to source"
};
Modified lines in public override void WriteLine(string message):
....
if (this.InformationPropertyCount == 0)
{
//Only treat message as an exception if it is not to be ignored
if (!m_MessagesToIgnore.Any(
x => this.Message.StartsWith(x, StringComparison.InvariantCultureIgnoreCase)))
{
PresentationTraceSources.DataBindingSource.Listeners.Remove(this);
throw new BindingException(this.Message,
new BindingExceptionInformation(this.Callstack,
System.DateTime.Parse(this.DateTime),
this.LogicalOperationStack, int.Parse(this.ProcessId),
int.Parse(this.ThreadId), long.Parse(this.Timestamp)));
}
else
{
//Ignore message, reset values
this.IsFirstWrite = true;
this.DetermineInformationPropertyCount();
}
}
}
You can use the trigger debugging feature of WPF Inspector. Just download the tool from codeplex and attach it to your running app. It also shows binding errors on the bottom of the window.
Very useful tool!
Here's a useful technique for debugging/tracing triggers effectively. It allows you to log all trigger actions along with the element being acted upon:
http://www.wpfmentor.com/2009/01/how-to-debug-triggers-using-trigger.html
This was very helpful to us but I wanted to add to those who find this useful that there is a utility that Microsoft provides with the sdk to read this file.
Found here: http://msdn.microsoft.com/en-us/library/ms732023.aspx
To open a trace file
1.Start Service Trace Viewer by using a command window to navigate to your
WCF installation location (C:\Program
Files\Microsoft
SDKs\Windows\v6.0\Bin), and then type
SvcTraceViewer.exe. (although we found ours in \v7.0\Bin)
Note: The Service Trace Viewer tool
can associate with two file types:
.svclog and .stvproj. You can use two
parameters in command line to register
and unregister the file extensions.
/register: register the association of
file extensions ".svclog" and
".stvproj" with SvcTraceViewer.exe
/unregister: unregister the
association of file extensions
".svclog" and ".stvproj" with
SvcTraceViewer.exe
1.When Service Trace Viewer starts, click File and then point to Open.
Navigate to the location where your
trace files are stored.
2.Double-click the trace file that you want to open.
Note: Press SHIFT while clicking
multiple trace files to select and
open them simultaneously. Service
Trace Viewer merges the content of all
files and presents one view. For
example, you can open trace files of
both client and service. This is
useful when you have enabled message
logging and activity propagation in
configuration. In this way, you can
examine message exchange between
client and service. You can also drag
multiple files into the viewer, or use
the Project tab. See the Managing
Project section for more details.
3.To add additional trace files to the collection that is open, click File
and then point to Add. In the window
that opens, navigate to the location
of the trace files and double-click
the file you want to add.
Also, as for the filtering of the log file, we found these this link extremely helpful:
http://msdn.microsoft.com/en-us/library/ms751526.aspx
For anyone like me looking for a pure programmatic way of enabling all WPF Tracing at a given Trace Level, here is a piece of code that does it. For reference, it's based on this article: Trace sources in WPF.
It doesn't requires a change in the app.config file, and it does not require to change the registry either.
This is how I use it, in some startup place (App, etc.):
....
#if DEBUG
WpfUtilities.SetTracing();
#endif
....
And here is the utility code (by default it sends all Warning to the Default Trace Listener):
public static void SetTracing()
{
SetTracing(SourceLevels.Warning, null);
}
public static void SetTracing(SourceLevels levels, TraceListener listener)
{
if (listener == null)
{
listener = new DefaultTraceListener();
}
// enable WPF tracing
PresentationTraceSources.Refresh();
// enable all WPF Trace sources (change this if you only want DataBindingSource)
foreach (PropertyInfo pi in typeof(PresentationTraceSources).GetProperties(BindingFlags.Static | BindingFlags.Public))
{
if (typeof(TraceSource).IsAssignableFrom(pi.PropertyType))
{
TraceSource ts = (TraceSource)pi.GetValue(null, null);
ts.Listeners.Add(listener);
ts.Switch.Level = levels;
}
}
}
My suggestion at 2021:
The Best way is to use Benoit Blanchon small library from Nuget
His original post at here: https://stackoverflow.com/a/19610384/6296708
His GitHub link and more info about how to use it + Nuget command: https://github.com/bblanchon/WpfBindingErrors
Its features (until now!):
throw exception on binding errors (+ line number)
If source Variable throw any exceptions, this library will catch it and show it.
Unit Test supports too!
Happy Coding!

Resources